Introduction

In Mammals, life starts when a male sperm cell fertilizes a female oocyte to form a zygote. This unique totipotent cell can give rise to a complete multicellular organism. This formation relies on a series of cellular differentiation and spatial organization events. Much insight into Mammalian development has come from the mouse, which benefits from a rapid development (21 days) and provides unrivalled accessibility to embryos. Regarding human development however, differences have emerged, and many questions remain unsolved (Mole et al, 2020). Thanks to the advent of single-cell multi-omics techniques, we have now access to the very intimate molecular processes occuring in the early human embryo. However, exploration of these data has been optimized, and notably, a careful integrated analysis of the different omics levels has not been conducted so far.

In this study, we propose to apply integrated bioinformatics analyses to transcriptomic and methylomic data of the peri-implantation human embryo, in order to gain insight into the interconnection of these different molecular levels. For this purpose, we use single-cell RNA-Seq and PBAT data from (Zhou et al, 2019), extracted from in vitro cultured human embryos from day 6 to 12 of development. These data are particularly suited for our analysis, because transcriptomic and DNA methylation data were obtained from the same cell.

First, we normalize the data to make them suitable for secondary analysis algorithms. Then, we start investigating each dataset separately. We determine inter-individual similarities by dimension reduction approaches such as Principal Component Analysis (PCA). We cross-validate the results with unsupervised hierarchical clustering.

Next, we perform integrated analysis of the two datasets with different approaches:

  • Multi-Omics Factor Analysis (MOFA)
  • Kernel computation

We follow the analysis by investigating similarities between gene expression and DNA methylation patterns through the sample this time. For this purpose, we use Weighted Gene Correlation Network Analysis (WGCNA) in order to identify correlated gene modules.

Finally, we consult selected databases for functional enrichment analysis, and draw conclusions and perspectives

Data preprocessing

Data exploration

These two datasets consist of 130 cells from the three earliest cell lineages:

  • the epiblast (EPI), which is pluripotent and give rise to the fetus
  • the primitive endoderm (PE), which produces the yolk sac
  • the trophectoderm (TE), which yields the placenta.

The table below summarizes the distribution of cells into lineages and embryonic days.

dna=as.matrix(fastRead2("embryo_dna_methylation_good.tsv"))
colnames(dna)=sub("^[Meth_Tri_hv_]+(*)", "\\1", colnames(dna))
dna[is.na(dna)] <- 0

rna=as.matrix(fastRead2("GSE109555_TrioSeq_TPM.txt"))
colnames(rna)=sub("^[Tri_hv_]+(*)", "\\1", colnames(rna))
rna=rna[,colnames(dna)]

metadata=fastRead2("embryo_metadata.tsv")[,c("Lineage","Day","Sex","Day_Emb")]
rownames(metadata)=sub("^[Meth_Tri_hv_]+(*)", "\\1", rownames(metadata))
colnames(metadata)=c("lineage","day","sex","embryo")
metadata=metadata[colnames(dna),]

row_lineage=NULL

for (lineage_i in levels(factor(metadata$lineage))){
  
  lineage_day=NULL
  
  for (day_i in levels(factor(metadata$day,levels = c("D6","D8","D10","D12")))){
    lineage_day=cbind(lineage_day,nrow(metadata[which(metadata$lineage==lineage_i & metadata$day==day_i),]))
  }
  
  row_lineage=rbind(row_lineage, lineage_day)
}

rownames(row_lineage)=levels(factor(metadata$lineage))
colnames(row_lineage)=levels(factor(metadata$day,levels = c("D6","D8","D10","D12")))
row_lineage=cbind(row_lineage,Total=rowSums(row_lineage))
kable(row_lineage, caption = "Table of embryo cell features")
Table of embryo cell features
D6 D8 D10 D12 Total
Epi 22 5 4 0 31
PE 15 14 14 2 45
TE 26 10 10 8 54
day=metadata[colnames(dna),"day"]
names(day)=colnames(dna)
day_colors=c("D10"="blue","D12"="red","D6"="orange","D8"="purple")
day_colors <- day_colors[as.factor(day)]
day_shape = c("D10"="square","D12"="circle","D6"= "diamond","D8"= "x") 
day_shape <- day_shape[as.factor(day)]
day_color_pca_umap=unique(day_colors)
names(day_color_pca_umap)=unique(names(day_colors))
day_shape_pca_umap=unique(day_shape)
names(day_shape_pca_umap)=unique(names(day_shape))
day_traits=model.matrix(~ 0 + day, data=as.data.frame(day))

lineage=metadata[colnames(dna),"lineage"]
names(lineage)=colnames(dna)
lineage_colors=c("Epi"="red","PE"="green","TE"="blue")
lineage_colors <- lineage_colors[as.factor(lineage)]
lineage_shape = c("Epi"="square","PE"="circle","TE"= "diamond") 
lineage_shape <- lineage_shape[as.factor(lineage)]
lineage_color_pca_umap=unique(lineage_colors)
names(lineage_color_pca_umap)=unique(names(lineage_colors))
lineage_shape_pca_umap=unique(lineage_shape)
names(lineage_shape_pca_umap)=unique(names(lineage_shape))
lineage_traits=model.matrix(~ 0 + lineage, data=as.data.frame(lineage))

traits=cbind(day_traits,lineage_traits)
traits_main=cbind(as.numeric(factor(metadata$lineage)),as.numeric(factor(metadata$day)),as.numeric(factor(metadata$sex)),as.numeric(factor(metadata$embryo)))
colnames(traits_main)=colnames(metadata)
rownames(traits_main)=rownames(metadata)

Normalisation of data distribution

Input data have been pre-processed to some extent: methylation levels range from 0 to 1 while RNA-Seq counts are given in FPKM. However, distribution of feature values do not follow a Gaussian law. We need to normalise the data prior to further processing, in order to increase the robustness of secondary analyses.

By looking at total measures per individual for each dataset, we observe that library sizes for DNA methyaltion differ within sample, likely due to technical issues related to sequencing depth. So we decide to standardize this size by scaling units to 1e6. In contrast, each library from RNA-Seq dataset has the same size as it corresponds to FPKM.

# Normalize library size

dna=dna*100 #this operation is only intended to facilitate visualization of data initially ranging from 0 to 1
dna=round(dna,2) #this operation is only intended to facilitate comparison of distribution between the two datasets, same rounding 
dna_upm=round(apply(dna,2,function(x){x/sum(x)*1e6}),2)

We can also filter out null and low-variance features, as these might not contain relevant information for differential analysis we plan to conduct.

We start creating tables that summarize quantitative measures per individual and per feature.

dna_upm_gene_stat_prenorm <- data.frame(
mean=apply(dna_upm,1,mean),
sd=apply(dna_upm,1,sd),
var=apply(dna_upm,1,sd)^2,
cv=apply(dna_upm,1,function(x){sd(x)/mean(x)}),
iqr=apply(dna_upm,1,function(x) quantile(x, probs=seq(0,1,0.05), na.rm=T))["75%",]-apply(dna_upm,1,function(x) quantile(x, probs=seq(0,1,0.05), na.rm=T))["25%",],
min=apply(dna_upm,1,min),
median=apply(dna_upm,1,median),
max=apply(dna_upm,1,max),
null = apply(dna_upm == 0, 1, sum, na.rm = TRUE)
)

rna_gene_stat_prenorm <- data.frame(
mean=apply(rna,1,mean),
sd=apply(rna,1,sd),
var=apply(rna,1,sd)^2,
cv=apply(rna,1,function(x){sd(x)/mean(x)}),
iqr=apply(rna,1,function(x) quantile(x, probs=seq(0,1,0.05), na.rm=T))["75%",]-apply(rna,1,function(x) quantile(x, probs=seq(0,1,0.05), na.rm=T))["25%",],
min=apply(rna,1,min),
median=apply(rna,1,median),
max=apply(rna,1,max),
null = apply(rna == 0, 1, sum, na.rm = TRUE)
)
## Data filtering: genes having at least 90% null values
meth_prom_null <- dna_upm_gene_stat_prenorm$null >= ncol(dna_upm)
transcript_null <- rna_gene_stat_prenorm$null >= ncol(rna)

meth_prom_low_var <- dna_upm_gene_stat_prenorm$var <= median(dna_upm_gene_stat_prenorm$var)
transcript_low_var <- rna_gene_stat_prenorm$var <= median(rna_gene_stat_prenorm$var)

meth_prom_subset = !(meth_prom_null | meth_prom_low_var)
transcript_subset = !(transcript_null | transcript_low_var)

dna_upm_subset = dna_upm[meth_prom_subset,]
rna_subset = rna[transcript_subset,]
hist(dna, breaks=100, main = "Input")
hist(log2(dna_upm), breaks=100, main = "Size standardisation & log-transformation")
hist(log2(dna_upm_subset), breaks=100, main = "Size standardisation, filtration & log-transformation")

Normalisation of gene promoter methylation level distribution

<center>**Normalisation of gene promoter methylation level distribution**</center><center>**Normalisation of gene promoter methylation level distribution**</center><center>**Normalisation of gene promoter methylation level distribution**</center>
hist(rna, breaks=100, main = "Input")
hist(log2(rna), breaks=100, main = "Log-transformation")
hist(log2(rna_subset), breaks=100, main = "Size standardisation, filtration & log-transformation")

Normalisation of gene promoter methylation levels

<center>**Normalisation of gene promoter methylation levels**</center><center>**Normalisation of gene promoter methylation levels**</center><center>**Normalisation of gene promoter methylation levels**</center>

When we plot input data for the two datasets, we observe a 0 inflation, which can have two origins:

  • biological: 0 expression or methylation, unlikely
  • technical: no detection of expression or methylation, likely

Moreover, we observe that distribution is also skewed by high values.

So for better visualization of the latent distribution, we apply log transformation of the data in order to remove 0 and reduce dynamic range of variables.

By doing this, we reveal that biologically related distribution of gene expression and DNA methylation levels at gene promoters, after filtering out low-variance features, both approximate the normal/Gaussian law.

It is interesting to mention that low-variance levels correspond to low methylated regions, and if not filtered out, yield a bimodal distribution.

meth_prom_sample_dist <- factoextra::get_dist(x =t(log2(dna_upm_subset+1)), method = "pearson")

meth_prom_sample_dist %>%
  hclust(method="ward.D2") %>%
  color_branches(k=2,col = c("#00FFFF","#FF0000"), groupLabels = T) -> meth_prom_sample_dend

labels_colors(meth_prom_sample_dend) <- rainbow(4)[factor(day[order.dendrogram(meth_prom_sample_dend)])]

plot(meth_prom_sample_dend)


meth_prom_sample_dist <- factoextra::get_dist(x =t(log2(dna_upm_subset+1)), method = "pearson")

meth_prom_sample_dist %>%
  hclust(method="ward.D2") %>%
  color_branches(k=2,col = c("#00FFFF","#FF0000"), groupLabels = T) -> meth_prom_sample_dend

labels_colors(meth_prom_sample_dend) <- rainbow(3)[factor(lineage[order.dendrogram(meth_prom_sample_dend)])]

plot(meth_prom_sample_dend)

Hierarchical clustering of embryo cells on gene promoter methylation

<center>**Hierarchical clustering of embryo cells on gene promoter methylation**</center><center>**Hierarchical clustering of embryo cells on gene promoter methylation**</center>
transcript_sample_dist <- factoextra::get_dist(x =t(log2(rna_subset+1)), method = "pearson")

transcript_sample_dist %>%
  hclust(method="ward.D2") %>%
  color_branches(k=10,col = c("blue","blue","green","blue","red","red","red","green","green","green"), groupLabels = T) -> transcript_sample_dend

labels_colors(transcript_sample_dend) <- rainbow(4)[factor(day[order.dendrogram(transcript_sample_dend)])]

plot(transcript_sample_dend)


transcript_sample_dist <- factoextra::get_dist(x =t(log2(rna_subset+1)), method = "pearson")

transcript_sample_dist %>%
  hclust(method="ward.D2") %>%
  color_branches(k=10,col = c("blue","blue","green","blue","red","red","red","green","green","green"), groupLabels = T) -> transcript_sample_dend

labels_colors(transcript_sample_dend) <- rainbow(3)[factor(lineage[order.dendrogram(transcript_sample_dend)])]

plot(transcript_sample_dend)

Hierarchical clustering of embryo cells on gene expression

<center>**Hierarchical clustering of embryo cells on gene expression**</center><center>**Hierarchical clustering of embryo cells on gene expression**</center>

We choose to calculate Pearson correlation distance between DNA methylated gene promoters, because this method produces the best Rand Adjusted Index (Jaskowiak et al, 2014). Then we apply hierarchical clustering with Ward method (ā€œward.D2ā€), as this most robustly identifies groups of individuals with similar quantitative traits. Indeed, this groups individuals according to variance instead of mean, contrary to other methods such as ā€œaverageā€ (Lawlor et al, 2016).

For methylation levels of gene promoters, we observe that the two main clusters segregate day 6 from later developmental stages. However, within the second main cluster, we see that subclusters well separate cell lineages. For subsequent analyses, we will consider analyzing day6 either together or separately from later stages, depending on the question to be addressed.

For gene expression, we observe that clusters nicely segregate cell lineages.

Dimensionality reduction based analysis of inter-individual vicinity

In order to investigate individual vicinity within sample, we perform a Principal Component Analysis (PCA), which reduces dimensions while keeping maximum of sample variance summarised in ā€œeigenvectorsā€ (Lever et al, 2017).

We also perform another dimensionality reduction analysis, the Uniform Manifold Approximation and Projection (UMAP), which is complementary to PCA, as it is nonlinear, and outperforms t-SNE (Kobak et al, 2019). A key parameter for the UMAP is the number of neighbours for each individual. In order to determine the most appropriate number of neighbours according to the latent structure of the data, we use the result of the hierarchical clustering of cells, cut at three clusters recapitulating developmental days or cell types relative to the dataset, and apply the mean number of cells per cluster.

dna_pca<-ACP(log2(dna_upm_subset+1))

dna_umap<-make.umap2(log2(dna_upm_subset+1),n_components = 3,n_neighbors = ncol(dna_upm_subset),min_dist = 0.2)#good

rna_pca<-ACP(log2(rna_subset+1))

rna_umap<-make.umap2(log2(rna_subset+1),n_components = 3,n_neighbors = ncol(rna_subset),min_dist = 0.2, metric= "correlation")#good

# save.image("2021_06_07_embryo_umap.RData")
load(file = "2021_06_07_embryo_umap.RData")

dna_pca_fig <- plot_ly(x=dna_pca$x[,"PC1"], y=dna_pca$x[,"PC2"], z=dna_pca$x[,"PC3"],color = ~names(day_colors), colors = day_color_pca_umap,alpha=0.6,scene='scene1')
dna_pca_fig <- dna_pca_fig %>% add_markers()

dna_umap_fig <- plot_ly(x=dna_umap[,1], y=dna_umap[,2], z=dna_umap[,3],color = ~names(day_colors), colors = day_color_pca_umap,alpha=0.6,symbol = ~names(lineage_shape), symbols=lineage_shape_pca_umap,scene='scene2')
dna_umap_fig <- dna_umap_fig %>% add_markers()

dna_pca_umap_fig <- subplot(dna_pca_fig, dna_umap_fig) 
dna_pca_umap_fig <- dna_pca_umap_fig %>% layout(title = "3D Subplots",
         scene1 = list(domain=list(x=c(0,0.5),y=c(0,1)),
                      aspectmode='cube'),
         scene2 = list(domain=list(x=c(0.5,1),y=c(0,1)),
                       aspectmode='cube'))

dna_pca_umap_fig

PCA and UMAP of embryo cells computed on gene promoter methylation

rna_pca_fig <- plot_ly(x=rna_pca$x[,"PC1"], y=rna_pca$x[,"PC2"], z=rna_pca$x[,"PC3"],color = ~names(lineage_colors), colors = lineage_color_pca_umap,alpha=0.6, scene='scene1')
rna_pca_fig <- rna_pca_fig %>% add_markers()

rna_umap_fig <- plot_ly(x=rna_umap[,1], y=rna_umap[,2], z=rna_umap[,3],color = ~names(lineage_colors), colors = lineage_color_pca_umap,alpha=0.6,symbol = ~names(day_shape), symbols=day_shape_pca_umap, scene='scene2')
rna_umap_fig <- rna_umap_fig %>% add_markers()

rna_pca_umap_fig <- subplot(rna_pca_fig, rna_umap_fig) 
rna_pca_umap_fig <- rna_pca_umap_fig %>% layout(title = "3D Subplots",
         scene1 = list(domain=list(x=c(0,0.5),y=c(0,1)),
                      aspectmode='cube'),
         scene2 = list(domain=list(x=c(0.5,1),y=c(0,1)),
                       aspectmode='cube'))

rna_pca_umap_fig

PCA and UMAP of embryo cells computed on gene expression

# save.image(paste0(timeNow(),"embryo_dna_rna_umap.RData"))

We observe that applying the ā€œcorrelationā€ metric based on Pearson distance to umap calculation provides much better results than euclidean distance.

PCA and UMAP yield groups of individuals in line with hierarchical clustering. For both analyses, we observe that gene promoter methylation mainly recapitulates developmental progression (embryonic days) while gene expression recapitulates cell lineages(EPI, PE, TE). This observation is in line with previously published results from (Zhou et al, 2019), as shown in the figure below:

ā€œPrincipal component analysis of DNA methylome data showed that these 130 cells formed 4 major clusters (Extended Data Fig. 8g, h), with a combination of the EPI, PE and TE at the blastocyst stage (day 6) as a single cluster, and the EPI, PE and TE beyond the blastocyst stage as another 3 separate clusters, suggesting that all of the 3 lineages showed considerable changes in DNA methylation soon after implantation.ā€

In terms of biology, this could mean that at day 6, global DNA methylation state results from epigenetic waves at early embryonic stages, prior to lineage specification. Later stages show rewriting of epigenetic marks specific to cell lineages, which have distinct transcriptomic signatures.

This observation is interesting as a combination of DNA methylation and gene expression levels might allow to assign developmental stage and lineage to single cells of the human embryo.

WGCNA

After considering inter-individual vicinity, we now investigate correlation within sample at gene promoter methylation and gene expression levels. For this purpose, we perform Weighted Gene Network Correlation Analysis (WGCNA). This algorithm searches for a latent structure within features, i.e modules of correlated genes at promoter methylation or transcriptional levels (Langfelder et al, 2008).

Parameter settings & WGCNA computation

First, we need to determine the most suitable WGCNA parameters to apply for each dataset. We notably need to choose the soft-power (soft thresholding power) which is used to power the adjacency matrix of genes, in order to reduce signal noise.

According to WGCNA package guidelines, it is not recommended filtering data prior to WGCNA. Indeed, this is designed to be an unsupervised analysis method that clusters genes based on their expression profiles (or methylation patterns, by extension). For instance, filtering genes by differential expression would lead to a set of correlated genes that will essentially form a few highly correlated modules. It would also completely invalidate the scale-free topology assumption, so choosing soft thresholding power by scale-free topology fit would fail. Therefore, we provide input data to the WGCNA algorithm.

#For WGCNA, we use the normalized expression matrix.

wgcna_exprDat=list(dna_upm_subset,rna_subset)
names(wgcna_exprDat)=c("dna","rna")

wgcna_datExpr=lapply(wgcna_exprDat,t) #We transpose the normalized expression #matrix, because WGCNA is used to find covariance etween genes, not between #sample, unlike PCA.

# Choose a set of soft-thresholding powers
powers = c(1:20)
# Call the network topology analysis function
sft = lapply(wgcna_datExpr,pickSoftThreshold, networkType = "signed", powerVector = powers, verbose = 5)
pickSoftThreshold: will use block size 3664.
 pickSoftThreshold: calculating connectivity for given powers...
   ..working on genes 1 through 3664 of 12210
   ..working on genes 3665 through 7328 of 12210
   ..working on genes 7329 through 10992 of 12210
   ..working on genes 10993 through 12210 of 12210
   Power SFT.R.sq  slope truncated.R.sq mean.k. median.k. max.k.
1      1    0.103  15.50          0.948 6130.00  6120.000 6470.0
2      2    0.468 -14.60          0.859 3130.00  3100.000 3610.0
3      3    0.888 -15.50          0.948 1620.00  1580.000 2120.0
4      4    0.935 -11.40          0.957  852.00   819.000 1300.0
5      5    0.946  -8.56          0.957  456.00   429.000  830.0
6      6    0.941  -6.89          0.953  249.00   228.000  551.0
7      7    0.945  -5.65          0.958  139.00   123.000  378.0
8      8    0.948  -4.79          0.967   79.10    67.900  266.0
9      9    0.946  -4.18          0.972   46.20    38.000  192.0
10    10    0.939  -3.74          0.975   27.70    21.600  141.0
11    11    0.925  -3.39          0.974   17.20    12.500  106.0
12    12    0.916  -3.07          0.971   11.00     7.400   80.4
13    13    0.896  -2.76          0.951    7.25     4.450   61.8
14    14    0.962  -2.36          0.985    4.98     2.750   50.4
15    15    0.956  -2.28          0.986    3.55     1.710   46.8
16    16    0.947  -2.19          0.970    2.62     1.070   43.9
17    17    0.948  -2.08          0.964    2.01     0.679   41.4
18    18    0.944  -1.98          0.956    1.60     0.438   39.3
19    19    0.934  -1.93          0.948    1.31     0.285   37.5
20    20    0.932  -1.85          0.944    1.11     0.187   35.8
pickSoftThreshold: will use block size 4698.
 pickSoftThreshold: calculating connectivity for given powers...
   ..working on genes 1 through 4698 of 9523
   ..working on genes 4699 through 9396 of 9523
   ..working on genes 9397 through 9523 of 9523
   Power SFT.R.sq  slope truncated.R.sq mean.k. median.k. max.k.
1      1  0.59200 49.200          0.721 4790.00  4790.000 4950.0
2      2  0.33100 17.400          0.796 2440.00  2440.000 2630.0
3      3  0.11800  6.430          0.924 1270.00  1260.000 1440.0
4      4  0.00227 -0.576          0.960  667.00   661.000  831.0
5      5  0.29100 -4.470          0.859  359.00   351.000  504.0
6      6  0.71200 -5.320          0.869  198.00   190.000  329.0
7      7  0.90100 -4.800          0.920  112.00   105.000  234.0
8      8  0.95100 -4.050          0.944   65.60    59.000  178.0
9      9  0.96700 -3.380          0.958   39.90    33.900  143.0
10    10  0.97100 -2.860          0.964   25.30    19.900  120.0
11    11  0.97200 -2.510          0.964   16.80    12.000  106.0
12    12  0.96400 -2.270          0.953   11.60     7.370   96.5
13    13  0.95400 -2.090          0.941    8.41     4.640   89.0
14    14  0.96500 -1.920          0.956    6.33     2.970   83.1
15    15  0.97500 -1.790          0.968    4.95     1.940   78.3
16    16  0.96700 -1.710          0.958    3.99     1.290   74.2
17    17  0.95200 -1.650          0.938    3.30     0.877   70.7
18    18  0.95400 -1.590          0.943    2.79     0.606   67.7
19    19  0.95600 -1.550          0.945    2.41     0.421   65.1
20    20  0.95400 -1.510          0.942    2.11     0.297   62.8
# Plot the results:
par(mfrow = c(1, 2));
options(repr.plot.width = 14, repr.plot.height = 10);

# Scale-free topology fit index as a function of the soft-thresholding power
plot.new()
plot(sft[["dna"]]$fitIndices[,1], -sign(sft[["dna"]]$fitIndices[,3])*sft[["dna"]]$fitIndices[,2],
xlab = "Soft Threshold (power)", ylab = "Scale Free Topology Model␣
,→Fit,signed R^2", type = "n",
main = paste("Scale independence"));
text(sft[["dna"]]$fitIndices[,1], -sign(sft[["dna"]]$fitIndices[,3])*sft[["dna"]]$fitIndices[,2],
labels = powers, cex = 0.9, col = "red");
# this line corresponds to using an R^2 cut-off of h
abline(h = 0.9, col = "red")
# Mean connectivity as a function of the soft-thresholding power
plot(sft[["dna"]]$fitIndices[,1], sft[["dna"]]$fitIndices[,5],
xlab = "Soft Threshold (power)", ylab = "Mean Connectivity", type = "n",
main = paste("Mean connectivity"));
text(sft[["dna"]]$fitIndices[,1], sft[["dna"]]$fitIndices[,5], labels = powers, cex = 0.9, col ="red")

# Scale-free topology fit index as a function of the soft-thresholding power
plot.new()
plot(sft[["rna"]]$fitIndices[,1], -sign(sft[["rna"]]$fitIndices[,3])*sft[["rna"]]$fitIndices[,2],
xlab = "Soft Threshold (power)", ylab = "Scale Free Topology Model␣
,→Fit,signed R^2", type = "n",
main = paste("Scale independence"));
text(sft[["rna"]]$fitIndices[,1], -sign(sft[["rna"]]$fitIndices[,3])*sft[["rna"]]$fitIndices[,2],
labels = powers, cex = 0.9, col = "red");
# this line corresponds to using an R^2 cut-off of h
abline(h = 0.9, col = "red")
# Mean connectivity as a function of the soft-thresholding power
plot(sft[["rna"]]$fitIndices[,1], sft[["rna"]]$fitIndices[,5],
xlab = "Soft Threshold (power)", ylab = "Mean Connectivity", type = "n",
main = paste("Mean connectivity"));
text(sft[["rna"]]$fitIndices[,1], sft[["rna"]]$fitIndices[,5], labels = powers, cex = 0.9, col ="red")

Setting parameters of WGCNA

<center>**Setting parameters of WGCNA**</center><center>**Setting parameters of WGCNA**</center><center>**Setting parameters of WGCNA**</center>

We choose a soft-power of 12 for both datasets, as this yields a > 0.9 scale-free topology fitting index (R2). Intuitively, we would have rather chosen a soft-power of 4 for the scPBAT and 7 for the scRNA-Seq datasets, as these correspond to the inflection points of the curves that also yield a > 0.9 R2. However, it is reported in WGCNA package guidelines that a minimum soft-power of 12 is advised for signed networks. This soft-power thresholding allows the correlation maximization with scale-free topology producing low mean connectivity.

One pitfall of WGCNA is overfitting the model. This could happen by setting soft-power too high.

Given the high number of genes, we fix a threshold of 100 genes per module, to avoid over-resolution yielding inflated small modules.

WGCNA on gene promoter methylation produces 50 modules, while it yields 39 modules of correlated gene expression. The grey groups correspond to genes that have not been assigned to any module.

WGCNA calculation

#################################################wgcna####################################################

# dna_net = blockwiseModules(wgcna_datExpr[["dna"]], power = 12, minModuleSize = 30, networkType = "signed",
#                         reassignThreshold = 1e-6, mergeCutHeight = 0.25,
#                         numericLabels = TRUE, pamRespectsDendro = FALSE,
#                         saveTOMs = TRUE, nThreads = 10,
#                         saveTOMFileBase = "dna",
#                         verbose = 0, corType="pearson")
# 
# rna_net = blockwiseModules(wgcna_datExpr[["rna"]], power = 12, minModuleSize = 30, networkType = "signed",
#                         reassignThreshold = 1e-6, mergeCutHeight = 0.25,
#                         numericLabels = TRUE, pamRespectsDendro = FALSE,
#                         saveTOMs = TRUE, nThreads = 10,
#                         saveTOMFileBase = "rna",
#                         verbose = 0, corType="pearson")

# save.image("2021_06_12_embryo_wgcna_computation.RData")
load("2021_06_12_embryo_wgcna_computation.RData")

# Convert labels to colors for plotting
dna_merged_colors = labels2colors(dna_net$colors)
rna_merged_colors = labels2colors(rna_net$colors)
# Plot the dendrogram and the module colors underneath
plot.new()
plotDendroAndColors(dna_net$dendrograms[[1]], dna_merged_colors[dna_net$blockGenes[[1]]],
                    "DNA methylation Module colors",
                    dendroLabels = FALSE, hang = 0.03,
                    addGuide = TRUE, guideHang = 0.05)

plotDendroAndColors(rna_net$dendrograms[[1]], rna_merged_colors[rna_net$blockGenes[[1]]],
                    "RNA methylation Module colors",
                    dendroLabels = FALSE, hang = 0.03,
                    addGuide = TRUE, guideHang = 0.05)

Visualization of WGCNA networks

Instead of Cytoscape, we use plotly to see it in 3D. What could be the third dimension ? interaction too (?)

Gene module membership & connectivity

 softConnectivity: FYI: connecitivty of genes with less than 44 valid samples will be returned as NA.
 ..calculating connectivities.. 
gene_promoter membership & connectivity of TTC5 module
IntraConnectivity InterConnectivity Membership
ZRANB3 38.48435 5.198102 0.9684532
GDF11 38.40717 5.226650 0.9666171
SPANXA1 38.63816 5.291323 0.9658512
SPANXA2 38.63816 5.291323 0.9658512
D0H9 38.17784 5.264974 0.9637463
 softConnectivity: FYI: connecitivty of genes with less than 44 valid samples will be returned as NA.
 ..calculating connectivities.. 
gene_transcript membership & connectivity of ATXN1 module
IntraConnectivity InterConnectivity Membership
BHLHE22 22.30803 5.856564 0.9860253
TCERG1L 20.28067 5.810619 0.9688330
RASSF3 20.01969 5.711293 0.9651837
SEC14L4 19.47583 5.648859 0.9612680
RAB6B 19.43805 5.497157 0.9602108

We calculate membership and connectivity of module genes and rename WGCNA modules with eigengenes defined by the highest membership to module.

Correlation heatmap of WGCNA modules & day of treatment

## DNA methylation
dna_moduleLabels = dna_net$colors
dna_moduleColors = labels2colors(dna_net$colors)
dna_MEs = dna_net$MEs;
dna_netree = dna_net$dendrograms[[1]];

# Define numbers of genes and samples
dna_nGenes = ncol(wgcna_datExpr[["dna"]]);
dna_nSamples = nrow(wgcna_datExpr[["dna"]]);
dna_MEs_eigengenes = moduleEigengenes(wgcna_datExpr[["dna"]], dna_moduleLabels)$eigengenes

dna_MEs_eigengenes=dna_MEs_eigengenes[,colnames(dna_MEs)]
colnames(dna_MEs_eigengenes)=paste0("ME_",gene_promoter_module_new_name)
colnames(dna_MEs_eigengenes)[length(colnames(dna_MEs_eigengenes))]=paste0(colnames(dna_MEs_eigengenes)[length(colnames(dna_MEs_eigengenes))],"_ME0")#On précise quel est le groupe "0", qui correspond aux gènes n'appartenant à aucun module.

dna_moduleTraitCor = cor(dna_MEs_eigengenes, traits, use = "p");
dna_moduleTraitPvalue = corPvalueStudent(dna_moduleTraitCor, dna_nSamples);

# Will display correlations and their p-values
dna_testMatrix =  paste(signif(dna_moduleTraitCor, 2), "\n(",
                           signif(dna_moduleTraitPvalue, 1), ")", sep = "");
dim(dna_testMatrix) = dim(dna_moduleTraitCor)
par(mar = c(6, 8, 1, 1));

# Heatmap(dna_moduleTraitCor,clustering_method_rows="ward.D2",clustering_method_columns="ward.D2")

Heatmap(dna_moduleTraitCor[,c("dayD6","dayD8","dayD10","dayD12","lineageEpi","lineagePE","lineageTE")],clustering_method_rows="ward.D2",cluster_columns = F)

dna_moduleTraitCor_main = cor(dna_MEs_eigengenes, traits_main, use = "p");
dna_moduleTraitPvalue_main = corPvalueStudent(dna_moduleTraitCor_main, dna_nSamples);

# Will display correlations and their p-values
dna_testMatrix =  paste(signif(dna_moduleTraitCor_main, 2), "\n(",
                           signif(dna_moduleTraitPvalue_main, 1), ")", sep = "");
dim(dna_testMatrix) = dim(dna_moduleTraitCor_main)
par(mar = c(6, 8, 1, 1));

Heatmap(dna_moduleTraitCor_main,clustering_method_rows="ward.D2",clustering_method_columns="ward.D2")

## RNA
rna_moduleLabels = rna_net$colors
rna_moduleColors = labels2colors(rna_net$colors)
rna_MEs = rna_net$MEs;
rna_netree = rna_net$dendrograms[[1]];

# Define numbers of genes and samples
rna_nGenes = ncol(wgcna_datExpr[["rna"]]);
rna_nSamples = nrow(wgcna_datExpr[["rna"]]);
rna_MEs_eigengenes = moduleEigengenes(wgcna_datExpr[["rna"]], rna_moduleLabels)$eigengenes

rna_MEs_eigengenes=rna_MEs_eigengenes[,colnames(rna_MEs)]
colnames(rna_MEs_eigengenes)=paste0("ME_",gene_transcript_module_new_name)
colnames(rna_MEs_eigengenes)[length(colnames(rna_MEs_eigengenes))]=paste0(colnames(rna_MEs_eigengenes)[length(colnames(rna_MEs_eigengenes))],"_ME0")#On précise quel est le groupe "0", qui correspond aux gènes n'appartenant à aucun module.

rna_moduleTraitCor = cor(rna_MEs_eigengenes, traits, use = "p");
rna_moduleTraitPvalue = corPvalueStudent(rna_moduleTraitCor, rna_nSamples);

# Will display correlations and their p-values
rna_testMatrix =  paste(signif(rna_moduleTraitCor, 2), "\n(",
                           signif(rna_moduleTraitPvalue, 1), ")", sep = "");
dim(rna_testMatrix) = dim(rna_moduleTraitCor)
par(mar = c(6, 8, 1, 1));

# Heatmap(rna_moduleTraitCor,clustering_method_rows="ward.D2",clustering_method_columns="ward.D2")

Heatmap(rna_moduleTraitCor[,c("dayD6","dayD8","dayD10","dayD12","lineageEpi","lineagePE","lineageTE")],clustering_method_rows="ward.D2",cluster_columns = F)

rna_moduleTraitCor_main = cor(rna_MEs_eigengenes, traits_main, use = "p");
rna_moduleTraitPvalue_main = corPvalueStudent(rna_moduleTraitCor_main, rna_nSamples);

# Will display correlations and their p-values
rna_testMatrix =  paste(signif(rna_moduleTraitCor_main, 2), "\n(",
                           signif(rna_moduleTraitPvalue_main, 1), ")", sep = "");
dim(rna_testMatrix) = dim(rna_moduleTraitCor_main)
par(mar = c(6, 8, 1, 1));

Heatmap(rna_moduleTraitCor_main,clustering_method_rows="ward.D2",clustering_method_columns="ward.D2")

WGCNA modules-trait correlation heatmap

<center>**WGCNA modules-trait correlation heatmap**</center><center>**WGCNA modules-trait correlation heatmap**</center><center>**WGCNA modules-trait correlation heatmap**</center><center>**WGCNA modules-trait correlation heatmap**</center>

We observe that modules of gene promoters and transcripts correlate nicely with development stage or cell lineage, showing almost exclusive patterns.

Integration of correlated gene and DNA methylation modules

We now perform an integrative approach on correlation networks between the two datasets, to investigate potential interconnections between genes and DNA methylation modules.

dna_rna_moduleTraitCor_main = cor(rna_MEs_eigengenes, dna_MEs_eigengenes, use = "p");
dna_rna_moduleTraitPvalue_main = corPvalueStudent(dna_rna_moduleTraitCor_main, dna_nSamples);

# Will display correlations and their p-values
dna_rna_testMatrix =  paste(signif(dna_rna_moduleTraitCor_main, 2), "\n(",
                           signif(dna_rna_moduleTraitPvalue_main, 1), ")", sep = "");
dim(dna_rna_testMatrix) = dim(dna_rna_moduleTraitCor_main)
par(mar = c(6, 8, 1, 1));

Heatmap(dna_rna_moduleTraitCor_main,clustering_method_rows="ward.D2",clustering_method_columns="ward.D2",name = "DNA-RNA Module\ncorrelation",row_title="WGCNA modules for gene expression", row_title_side = "right", row_title_gp = gpar(fontsize = 13.2, fontface="bold"), row_names_gp = gpar(col = c("purple")), column_title="WGCNA modules for gene promoter methylation", column_title_side = "bottom",column_title_gp = gpar(fontsize = 13.2, fontface="bold"), column_names_gp = gpar(col = c("#13d370")))

dna_rna_module_correlation_points=NULL
names_dna_rna_modules=NULL

for (i in seq(nrow(dna_rna_moduleTraitCor_main))){
  
  dna_rna_module_correlation_points=c(dna_rna_module_correlation_points,dna_rna_moduleTraitCor_main[i,])
  
  names_dna_rna_modules=c(names_dna_rna_modules,paste0(rep(rownames(dna_rna_moduleTraitCor_main)[i],ncol(dna_rna_moduleTraitCor_main)),"-",colnames(dna_rna_moduleTraitCor_main)))
  
}

dna_rna_module_correlation_plot=NULL
dna_rna_module_correlation_plot$module_pair=names_dna_rna_modules
dna_rna_module_correlation_plot$cor_value=dna_rna_module_correlation_points
dna_rna_module_correlation_plot=as.data.frame(dna_rna_module_correlation_plot)

ggplot(dna_rna_module_correlation_plot,aes(x=module_pair, y=cor_value, label=module_pair))+
  geom_point()+
  geom_text(aes(label=ifelse(cor_value > 0.5,module_pair,'')),hjust=0,vjust=0)+
  geom_hline(yintercept=0.5, linetype="solid", 
                color = "red", size=1)

Multi-Omics WGCNA modules correlation

<center>**Multi-Omics WGCNA modules correlation**</center><center>**Multi-Omics WGCNA modules correlation**</center>

We select the top most correlated or anti-correlated gene promoter and transcript modules for further investigation. We foucs on 3 pairs of top correlated transcript-pomoter modules, that characterize some lineage-stage traits:

  • ME_RPS23-ME_SNORD116.8 (Epi-day6)
  • ME_VWCE-ME_SPACA5B (PE-day8)
  • ME_UBE2E2-ME_MXI1 (TE-day8)

co-expression similarity and ā€œco-methylation similarityā€

Visualization of WGCNA module network with Cytoscape

Functional enrichment on WGCNA selected modules

We set a p-value threshold of 0.1 for significant process enrichment, as modules ~ 40 genes fails to produce significant results under a 0.05 threshold.

# We get the genes from selected WGCNA modules
gene_promoter_module_membership_go=gene_promoter_module_membership
names(gene_promoter_module_membership_go)=gene_promoter_module_new_name

gene_transcript_module_membership_go=gene_transcript_module_membership
names(gene_transcript_module_membership_go)=gene_transcript_module_new_name

module_promoters_epi_d6 <- rownames(gene_promoter_module_membership_go[["STARD10"]])
module_transcripts_epi_d6 <- rownames(gene_transcript_module_membership_go[["RP9P"]])


module_promoters_pe_d8 <- rownames(gene_promoter_module_membership_go[["SPACA5B"]])
module_transcripts_pe_d8 <- rownames(gene_transcript_module_membership_go[["VWCE"]])

module_promoters_te_d6 <- rownames(gene_promoter_module_membership_go[["SNORD76"]])
module_transcripts_te_d6 <- rownames(gene_transcript_module_membership_go[["WNT7B"]])

Network visualization

As they are the main drivers of cell fate progression, we focus on transcription factor interactions. We query the TF2DNA database on computed interactions in human based on DNA binding motives. Transcription factors are preceded with the ā€œTFā€ symbol and yield a list of regulated genes they interact with, while other classes of genes do not.

# fastWrite(c(module_promoters_epi_d6,module_transcripts_epi_d6),file="module_promoters_module_transcripts_epi_d6.txt",col.names = F)
# fastWrite(c(module_promoters_pe_d8,module_transcripts_pe_d8),file="module_promoters_module_transcripts_pe_d8.txt",col.names = F)
# fastWrite(c(module_promoters_te_d6,module_transcripts_te_d6),file="module_promoters_module_transcripts_te_d6.txt",col.names = F)

epi_interaction_files = list.files(pattern="^epi_d6_*")
sub1=sub("^[epi_d6_]+(*)", "\\1", epi_interaction_files)
sub2=sub("\\.csv.*", "", sub1)
epi_module_interactions = lapply(epi_interaction_files, fastRead2)
names(epi_module_interactions)=sub2

pe_interaction_files = list.files(pattern="^pe_d8_*")
sub1=sub("^[pe_d8_]+(*)", "\\1", pe_interaction_files)
sub2=sub("\\.csv.*", "", sub1)
pe_module_interactions = lapply(pe_interaction_files, fastRead2)
names(pe_module_interactions)=sub2

te_interaction_files = list.files(pattern="^te_d6_*")
sub1=sub("^[te_d6_]+(*)", "\\1", te_interaction_files)
sub2=sub("\\.csv.*", "", sub1)
te_module_interactions = lapply(te_interaction_files, fastRead2)
names(te_module_interactions)=sub2

epi_d6_dna_rna_module_interaction=NULL
epi_d6_interaction_df=NULL
for (i in names(epi_module_interactions)){
  epi_d6_dna_rna_module_interaction[[i]]=cbind(source_node=rep(names(epi_module_interactions[i]),nrow(epi_module_interactions[[i]])),target_node=rownames(epi_module_interactions[[i]]),p_value=epi_module_interactions[[i]]$p_value,chromosome=epi_module_interactions[[i]]$chromosome_name,downstream_gene=epi_module_interactions[[i]]$downstream)

  epi_d6_interaction_df=rbind(epi_d6_interaction_df,epi_d6_dna_rna_module_interaction[[i]])
}

pe_d8_dna_rna_module_interaction=NULL
pe_d8_interaction_df=NULL
for (i in names(pe_module_interactions)){
  pe_d8_dna_rna_module_interaction[[i]]=cbind(source_node=rep(names(pe_module_interactions[i]),nrow(pe_module_interactions[[i]])),target_node=rownames(pe_module_interactions[[i]]),p_value=pe_module_interactions[[i]]$p_value,chromosome=pe_module_interactions[[i]]$chromosome_name,downstream_gene=pe_module_interactions[[i]]$downstream)

  pe_d8_interaction_df=rbind(pe_d8_interaction_df,pe_d8_dna_rna_module_interaction[[i]])
}

te_d6_dna_rna_module_interaction=NULL
te_d6_interaction_df=NULL
for (i in names(te_module_interactions)){
  te_d6_dna_rna_module_interaction[[i]]=cbind(source_node=rep(names(te_module_interactions[i]),nrow(te_module_interactions[[i]])),target_node=rownames(te_module_interactions[[i]]),p_value=te_module_interactions[[i]]$p_value,chromosome=te_module_interactions[[i]]$chromosome_name,downstream_gene=te_module_interactions[[i]]$downstream)

  te_d6_interaction_df=rbind(te_d6_interaction_df,te_d6_dna_rna_module_interaction[[i]])
}

epi_d6_dna_adjacency <- adjacency(wgcna_datExpr[["dna"]],
selectCols = NULL,
type = "signed",
power = 12, #set soft-power to 12 as for module computation
corFnc = "cor", corOptions = list(use = "p"),
weights = NULL,
distFnc = "dist", distOptions = "method = 'ward.D2'",
weightArgNames = c("weights.x", "weights.y"))

epi_d6_rna_adjacency <- adjacency(wgcna_datExpr[["rna"]],
selectCols = NULL,
type = "signed",
power = 12, #set soft-power to 12 as for module computation
corFnc = "cor", corOptions = list(use = "p"),
weights = NULL,
distFnc = "dist", distOptions = "method = 'ward.D2'",
weightArgNames = c("weights.x", "weights.y"))

pe_d8_dna_adjacency <- adjacency(wgcna_datExpr[["dna"]],
selectCols = NULL,
type = "signed",
power = 12, #set soft-power to 12 as for module computation
corFnc = "cor", corOptions = list(use = "p"),
weights = NULL,
distFnc = "dist", distOptions = "method = 'ward.D2'",
weightArgNames = c("weights.x", "weights.y"))

pe_d8_rna_adjacency <- adjacency(wgcna_datExpr[["rna"]],
selectCols = NULL,
type = "signed",
power = 12, #set soft-power to 12 as for module computation
corFnc = "cor", corOptions = list(use = "p"),
weights = NULL,
distFnc = "dist", distOptions = "method = 'ward.D2'",
weightArgNames = c("weights.x", "weights.y"))

te_d6_dna_adjacency <- adjacency(wgcna_datExpr[["dna"]],
selectCols = NULL,
type = "signed",
power = 12, #set soft-power to 12 as for module computation
corFnc = "cor", corOptions = list(use = "p"),
weights = NULL,
distFnc = "dist", distOptions = "method = 'ward.D2'",
weightArgNames = c("weights.x", "weights.y"))

te_d6_rna_adjacency <- adjacency(wgcna_datExpr[["rna"]],
selectCols = NULL,
type = "signed",
power = 12, #set soft-power to 12 as for module computation
corFnc = "cor", corOptions = list(use = "p"),
weights = NULL,
distFnc = "dist", distOptions = "method = 'ward.D2'",
weightArgNames = c("weights.x", "weights.y"))
epi_d6_dna_adjacency_selected=epi_d6_dna_adjacency[module_promoters_epi_d6,module_promoters_epi_d6]
epi_d6_rna_adjacency_selected=epi_d6_rna_adjacency[module_transcripts_epi_d6,module_transcripts_epi_d6]

pe_d8_dna_adjacency_selected=pe_d8_dna_adjacency[module_promoters_pe_d8,module_promoters_pe_d8]
pe_d8_rna_adjacency_selected=pe_d8_rna_adjacency[module_transcripts_pe_d8,module_transcripts_pe_d8]

te_d6_dna_adjacency_selected=te_d6_dna_adjacency[module_promoters_te_d6,module_promoters_te_d6]
te_d6_rna_adjacency_selected=te_d6_rna_adjacency[module_transcripts_te_d6,module_transcripts_te_d6]
module_promoters_epi_d6_coord=NULL
module_promoters_epi_d6_coord$source_node=module_promoters_epi_d6
module_promoters_epi_d6_coord$x_source=rep(1,length(module_promoters_epi_d6))
module_promoters_epi_d6_coord$y_source=seq(to=(100/length(module_promoters_epi_d6)), from=100, by=-(100/length(module_promoters_epi_d6)))
module_promoters_epi_d6_coord=as.data.frame(module_promoters_epi_d6_coord,col.names=names(module_promoters_epi_d6_coord))

module_transcripts_epi_d6_coord=NULL
module_transcripts_epi_d6_coord$source_node=module_transcripts_epi_d6
module_transcripts_epi_d6_coord$x_source=rep(2,length(module_transcripts_epi_d6))
module_transcripts_epi_d6_coord$y_source=seq(to=(100/length(module_transcripts_epi_d6)), from=100, by=-(100/length(module_transcripts_epi_d6)))
module_transcripts_epi_d6_coord=as.data.frame(module_transcripts_epi_d6_coord,col.names=names(module_transcripts_epi_d6_coord))

epi_modules_coord=rbind(module_promoters_epi_d6_coord,module_transcripts_epi_d6_coord)


epi_d6_module_arrows=NULL

  
epi_arrow_dna_dna=epi_d6_interaction_df[epi_d6_interaction_df[,"source_node"]%in%module_promoters_epi_d6 & epi_d6_interaction_df[,"target_node"]%in%module_promoters_epi_d6,]
epi_arrow_dna_dna=cbind(epi_arrow_dna_dna,x_arrow=rep(1.13,nrow(epi_arrow_dna_dna)),xend_arrow=rep(1.13,nrow(epi_arrow_dna_dna)))

y=NULL

for (i in epi_arrow_dna_dna[,"source_node"]){
  
  y=c(y,module_promoters_epi_d6_coord$y_source[module_promoters_epi_d6_coord$source_node==i])  
  
}

yend=NULL

for (i in epi_arrow_dna_dna[,"target_node"]){
  
  yend=c(yend,module_promoters_epi_d6_coord$y_source[module_promoters_epi_d6_coord$source_node==i])  
  
}

epi_arrow_dna_dna=cbind(epi_arrow_dna_dna,y_arrow=y,yend_arrow=yend)

epi_arrow_dna_rna=epi_d6_interaction_df[epi_d6_interaction_df[,"source_node"]%in%module_promoters_epi_d6 & epi_d6_interaction_df[,"target_node"]%in%module_transcripts_epi_d6,]
epi_arrow_dna_rna=cbind(epi_arrow_dna_rna,x_arrow=rep(1.13,nrow(epi_arrow_dna_rna)),xend_arrow=rep(1.98,nrow(epi_arrow_dna_rna)))

y=NULL

for (i in epi_arrow_dna_rna[,"source_node"]){
  
  y=c(y,module_promoters_epi_d6_coord$y_source[module_promoters_epi_d6_coord$source_node==i])  
  
}

yend=NULL

for (i in epi_arrow_dna_rna[,"target_node"]){
  
  yend=c(yend,module_transcripts_epi_d6_coord$y_source[module_transcripts_epi_d6_coord$source_node==i])  
  
}

epi_arrow_dna_rna=cbind(epi_arrow_dna_rna,y_arrow=y,yend_arrow=yend)

epi_arrow_rna_rna=epi_d6_interaction_df[epi_d6_interaction_df[,"source_node"]%in%module_transcripts_epi_d6 & epi_d6_interaction_df[,"target_node"]%in%module_transcripts_epi_d6,]
epi_arrow_rna_rna=cbind(epi_arrow_rna_rna,x_arrow=rep(1.98,nrow(epi_arrow_rna_rna)),xend_arrow=rep(1.98,nrow(epi_arrow_rna_rna)))


y=NULL

for (i in epi_arrow_rna_rna[,"source_node"]){
  
  y=c(y,module_transcripts_epi_d6_coord$y_source[module_transcripts_epi_d6_coord$source_node==i])  
  
}

yend=NULL

for (i in epi_arrow_rna_rna[,"target_node"]){
  
  yend=c(yend,module_transcripts_epi_d6_coord$y_source[module_transcripts_epi_d6_coord$source_node==i])  
  
}

epi_arrow_rna_rna=cbind(epi_arrow_rna_rna,y_arrow=y,yend_arrow=yend)


epi_arrow_rna_dna=epi_d6_interaction_df[epi_d6_interaction_df[,"source_node"]%in%module_transcripts_epi_d6 & epi_d6_interaction_df[,"target_node"]%in%module_promoters_epi_d6,]
epi_arrow_rna_dna=cbind(epi_arrow_rna_dna,x_arrow=rep(1.98,nrow(epi_arrow_rna_dna)),xend_arrow=rep(1.13,nrow(epi_arrow_rna_dna)))


y=NULL

for (i in epi_arrow_rna_dna[,"source_node"]){
  
  y=c(y,module_transcripts_epi_d6_coord$y_source[module_transcripts_epi_d6_coord$source_node==i])  
  
}

yend=NULL

for (i in epi_arrow_rna_dna[,"target_node"]){
  
  yend=c(yend,module_promoters_epi_d6_coord$y_source[module_promoters_epi_d6_coord$source_node==i])  
  
}

epi_arrow_rna_dna=cbind(epi_arrow_rna_dna,y_arrow=y,yend_arrow=yend)


epi_interaction_arrows=rbind(epi_arrow_dna_dna,epi_arrow_dna_rna,epi_arrow_rna_rna,epi_arrow_rna_dna)

epi_modules_coord$module=ifelse(epi_modules_coord$x_source==1,"dna","rna")
epi_interaction_arrows=cbind(epi_interaction_arrows,module_origin=ifelse(epi_interaction_arrows[,"x_arrow"]==1.13,"dna","rna"))

epi_modules_interaction_plot=ggplot(epi_modules_coord,aes(x=x_source, y=y_source, label=source_node))+
  geom_text(aes(label=source_node, color=as.character(module)),hjust=0,vjust=0)+
  geom_point(aes(x = ifelse(x_source==1,1.13,1.98),y=y_source, color=as.character(module)))+
  geom_curve(data=as.data.frame(epi_interaction_arrows),mapping=aes(x=as.numeric(epi_interaction_arrows[,"x_arrow"]),y=as.numeric(epi_interaction_arrows[,"y_arrow"]),xend=as.numeric(epi_interaction_arrows[,"xend_arrow"]),yend=as.numeric(epi_interaction_arrows[,"yend_arrow"]),color=as.character(epi_interaction_arrows[,"module_origin"])), curvature = -0.2, arrow=arrow(), size=1)+
  scale_color_manual(values=c("#13d370","purple"))



module_promoters_pe_d8_coord=NULL
module_promoters_pe_d8_coord$source_node=module_promoters_pe_d8
module_promoters_pe_d8_coord$x_source=rep(1,length(module_promoters_pe_d8))
module_promoters_pe_d8_coord$y_source=seq(to=(100/length(module_promoters_pe_d8)), from=100, by=-(100/length(module_promoters_pe_d8)))
module_promoters_pe_d8_coord=as.data.frame(module_promoters_pe_d8_coord,col.names=names(module_promoters_pe_d8_coord))

module_transcripts_pe_d8_coord=NULL
module_transcripts_pe_d8_coord$source_node=module_transcripts_pe_d8
module_transcripts_pe_d8_coord$x_source=rep(2,length(module_transcripts_pe_d8))
module_transcripts_pe_d8_coord$y_source=seq(to=(100/length(module_transcripts_pe_d8)), from=100, by=-(100/length(module_transcripts_pe_d8)))
module_transcripts_pe_d8_coord=as.data.frame(module_transcripts_pe_d8_coord,col.names=names(module_transcripts_pe_d8_coord))

pe_modules_coord=rbind(module_promoters_pe_d8_coord,module_transcripts_pe_d8_coord)

pe_d8_module_arrows=NULL

  
pe_arrow_dna_dna=pe_d8_interaction_df[pe_d8_interaction_df[,"source_node"]%in%module_promoters_pe_d8 & pe_d8_interaction_df[,"target_node"]%in%module_promoters_pe_d8,]
pe_arrow_dna_dna=cbind(pe_arrow_dna_dna,x_arrow=rep(1.13,nrow(pe_arrow_dna_dna)),xend_arrow=rep(1.13,nrow(pe_arrow_dna_dna)))

y=NULL

for (i in pe_arrow_dna_dna[,"source_node"]){
  
  y=c(y,module_promoters_pe_d8_coord$y_source[module_promoters_pe_d8_coord$source_node==i])  
  
}

yend=NULL

for (i in pe_arrow_dna_dna[,"target_node"]){
  
  yend=c(yend,module_promoters_pe_d8_coord$y_source[module_promoters_pe_d8_coord$source_node==i])  
  
}

pe_arrow_dna_dna=cbind(pe_arrow_dna_dna,y_arrow=y,yend_arrow=yend)

pe_arrow_dna_rna=pe_d8_interaction_df[pe_d8_interaction_df[,"source_node"]%in%module_promoters_pe_d8 & pe_d8_interaction_df[,"target_node"]%in%module_transcripts_pe_d8,]
pe_arrow_dna_rna=cbind(pe_arrow_dna_rna,x_arrow=rep(1.13,nrow(pe_arrow_dna_rna)),xend_arrow=rep(1.98,nrow(pe_arrow_dna_rna)))

y=NULL

for (i in pe_arrow_dna_rna[,"source_node"]){
  
  y=c(y,module_promoters_pe_d8_coord$y_source[module_promoters_pe_d8_coord$source_node==i])  
  
}

yend=NULL

for (i in pe_arrow_dna_rna[,"target_node"]){
  
  yend=c(yend,module_transcripts_pe_d8_coord$y_source[module_transcripts_pe_d8_coord$source_node==i])  
  
}

pe_arrow_dna_rna=cbind(pe_arrow_dna_rna,y_arrow=y,yend_arrow=yend)

pe_arrow_rna_rna=pe_d8_interaction_df[pe_d8_interaction_df[,"source_node"]%in%module_transcripts_pe_d8 & pe_d8_interaction_df[,"target_node"]%in%module_transcripts_pe_d8,]
pe_arrow_rna_rna=cbind(pe_arrow_rna_rna,x_arrow=rep(1.98,nrow(pe_arrow_rna_rna)),xend_arrow=rep(1.98,nrow(pe_arrow_rna_rna)))


y=NULL

for (i in pe_arrow_rna_rna[,"source_node"]){
  
  y=c(y,module_transcripts_pe_d8_coord$y_source[module_transcripts_pe_d8_coord$source_node==i])  
  
}

yend=NULL

for (i in pe_arrow_rna_rna[,"target_node"]){
  
  yend=c(yend,module_transcripts_pe_d8_coord$y_source[module_transcripts_pe_d8_coord$source_node==i])  
  
}

pe_arrow_rna_rna=cbind(pe_arrow_rna_rna,y_arrow=y,yend_arrow=yend)


pe_arrow_rna_dna=pe_d8_interaction_df[pe_d8_interaction_df[,"source_node"]%in%module_transcripts_pe_d8 & pe_d8_interaction_df[,"target_node"]%in%module_promoters_pe_d8,]
pe_arrow_rna_dna=cbind(pe_arrow_rna_dna,x_arrow=rep(1.98,nrow(pe_arrow_rna_dna)),xend_arrow=rep(1.13,nrow(pe_arrow_rna_dna)))


y=NULL

for (i in pe_arrow_rna_dna[,"source_node"]){
  
  y=c(y,module_transcripts_pe_d8_coord$y_source[module_transcripts_pe_d8_coord$source_node==i])  
  
}

yend=NULL

for (i in pe_arrow_rna_dna[,"target_node"]){
  
  yend=c(yend,module_promoters_pe_d8_coord$y_source[module_promoters_pe_d8_coord$source_node==i])  
  
}

pe_arrow_rna_dna=cbind(pe_arrow_rna_dna,y_arrow=y,yend_arrow=yend)


pe_interaction_arrows=rbind(pe_arrow_dna_dna,pe_arrow_dna_rna,pe_arrow_rna_rna,pe_arrow_rna_dna)


pe_modules_coord$module=ifelse(pe_modules_coord$x_source==1,"dna","rna")
pe_interaction_arrows=cbind(pe_interaction_arrows,module_origin=ifelse(pe_interaction_arrows[,"x_arrow"]==1.13,"dna","rna"))

pe_modules_interaction_plot=ggplot(pe_modules_coord,aes(x=x_source, y=y_source, label=source_node))+
  geom_text(aes(label=source_node, color=as.character(module)),hjust=0,vjust=0)+
  geom_point(aes(x = ifelse(x_source==1,1.13,1.98),y=y_source, color=as.character(module)))+
  geom_curve(data=as.data.frame(pe_interaction_arrows),mapping=aes(x=as.numeric(pe_interaction_arrows[,"x_arrow"]),y=as.numeric(pe_interaction_arrows[,"y_arrow"]),xend=as.numeric(pe_interaction_arrows[,"xend_arrow"]),yend=as.numeric(pe_interaction_arrows[,"yend_arrow"]),color=as.character(pe_interaction_arrows[,"module_origin"])), curvature = -0.2, arrow=arrow(), size=1)+
  scale_color_manual(values=c("#13d370","purple"))





module_promoters_te_d6_coord=NULL
module_promoters_te_d6_coord$source_node=module_promoters_te_d6
module_promoters_te_d6_coord$x_source=rep(1,length(module_promoters_te_d6))
module_promoters_te_d6_coord$y_source=seq(to=(100/length(module_promoters_te_d6)), from=100, by=-(100/length(module_promoters_te_d6)))
module_promoters_te_d6_coord=as.data.frame(module_promoters_te_d6_coord,col.names=names(module_promoters_te_d6_coord))

module_transcripts_te_d6_coord=NULL
module_transcripts_te_d6_coord$source_node=module_transcripts_te_d6
module_transcripts_te_d6_coord$x_source=rep(2,length(module_transcripts_te_d6))
module_transcripts_te_d6_coord$y_source=seq(to=(100/length(module_transcripts_te_d6)), from=100, by=-(100/length(module_transcripts_te_d6)))
module_transcripts_te_d6_coord=as.data.frame(module_transcripts_te_d6_coord,col.names=names(module_transcripts_te_d6_coord))

te_modules_coord=rbind(module_promoters_te_d6_coord,module_transcripts_te_d6_coord)

te_d6_module_arrows=NULL

  
te_arrow_dna_dna=te_d6_interaction_df[te_d6_interaction_df[,"source_node"]%in%module_promoters_te_d6 & te_d6_interaction_df[,"target_node"]%in%module_promoters_te_d6,]
te_arrow_dna_dna=cbind(te_arrow_dna_dna,x_arrow=rep(1.13,nrow(te_arrow_dna_dna)),xend_arrow=rep(1.13,nrow(te_arrow_dna_dna)))

y=NULL

for (i in te_arrow_dna_dna[,"source_node"]){
  
  y=c(y,module_promoters_te_d6_coord$y_source[module_promoters_te_d6_coord$source_node==i])  
  
}

yend=NULL

for (i in te_arrow_dna_dna[,"target_node"]){
  
  yend=c(yend,module_promoters_te_d6_coord$y_source[module_promoters_te_d6_coord$source_node==i])  
  
}

te_arrow_dna_dna=cbind(te_arrow_dna_dna,y_arrow=y,yend_arrow=yend)

te_arrow_dna_rna=te_d6_interaction_df[te_d6_interaction_df[,"source_node"]%in%module_promoters_te_d6 & te_d6_interaction_df[,"target_node"]%in%module_transcripts_te_d6,]
te_arrow_dna_rna=cbind(te_arrow_dna_rna,x_arrow=rep(1.13,nrow(te_arrow_dna_rna)),xend_arrow=rep(1.98,nrow(te_arrow_dna_rna)))

y=NULL

for (i in te_arrow_dna_rna[,"source_node"]){
  
  y=c(y,module_promoters_te_d6_coord$y_source[module_promoters_te_d6_coord$source_node==i])  
  
}

yend=NULL

for (i in te_arrow_dna_rna[,"target_node"]){
  
  yend=c(yend,module_transcripts_te_d6_coord$y_source[module_transcripts_te_d6_coord$source_node==i])  
  
}

te_arrow_dna_rna=cbind(te_arrow_dna_rna,y_arrow=y,yend_arrow=yend)

te_arrow_rna_rna=te_d6_interaction_df[te_d6_interaction_df[,"source_node"]%in%module_transcripts_te_d6 & te_d6_interaction_df[,"target_node"]%in%module_transcripts_te_d6,]
te_arrow_rna_rna=cbind(te_arrow_rna_rna,x_arrow=rep(1.98,nrow(te_arrow_rna_rna)),xend_arrow=rep(1.98,nrow(te_arrow_rna_rna)))


y=NULL

for (i in te_arrow_rna_rna[,"source_node"]){
  
  y=c(y,module_transcripts_te_d6_coord$y_source[module_transcripts_te_d6_coord$source_node==i])  
  
}

yend=NULL

for (i in te_arrow_rna_rna[,"target_node"]){
  
  yend=c(yend,module_transcripts_te_d6_coord$y_source[module_transcripts_te_d6_coord$source_node==i])  
  
}

te_arrow_rna_rna=cbind(te_arrow_rna_rna,y_arrow=y,yend_arrow=yend)


te_arrow_rna_dna=te_d6_interaction_df[te_d6_interaction_df[,"source_node"]%in%module_transcripts_te_d6 & te_d6_interaction_df[,"target_node"]%in%module_promoters_te_d6,]
te_arrow_rna_dna=cbind(te_arrow_rna_dna,x_arrow=rep(1.98,nrow(te_arrow_rna_dna)),xend_arrow=rep(1.13,nrow(te_arrow_rna_dna)))


y=NULL

for (i in te_arrow_rna_dna[,"source_node"]){
  
  y=c(y,module_transcripts_te_d6_coord$y_source[module_transcripts_te_d6_coord$source_node==i])  
  
}

yend=NULL

for (i in te_arrow_rna_dna[,"target_node"]){
  
  yend=c(yend,module_promoters_te_d6_coord$y_source[module_promoters_te_d6_coord$source_node==i])  
  
}

te_arrow_rna_dna=cbind(te_arrow_rna_dna,y_arrow=y,yend_arrow=yend)


te_interaction_arrows=rbind(te_arrow_dna_dna,te_arrow_dna_rna,te_arrow_rna_rna,te_arrow_rna_dna)


te_modules_coord$module=ifelse(te_modules_coord$x_source==1,"dna","rna")
te_interaction_arrows=cbind(te_interaction_arrows,module_origin=ifelse(te_interaction_arrows[,"x_arrow"]==1.13,"dna","rna"))

te_modules_interaction_plot=ggplot(te_modules_coord,aes(x=x_source, y=y_source, label=source_node))+
  geom_text(aes(label=source_node, color=as.character(module)),hjust=0,vjust=0)+
  geom_point(aes(x = ifelse(x_source==1,1.13,1.98),y=y_source, color=as.character(module)))+
  geom_curve(data=as.data.frame(te_interaction_arrows),mapping=aes(x=as.numeric(te_interaction_arrows[,"x_arrow"]),y=as.numeric(te_interaction_arrows[,"y_arrow"]),xend=as.numeric(te_interaction_arrows[,"xend_arrow"]),yend=as.numeric(te_interaction_arrows[,"yend_arrow"]),color=as.character(te_interaction_arrows[,"module_origin"])), curvature = -0.2, arrow=arrow(), size=1)+
  scale_color_manual(values=c("#13d370","purple"))




epi_d6_dna_size = NULL

for (i in module_promoters_epi_d6){
  epi_d6_dna_size=c(epi_d6_dna_size,sum(epi_interaction_arrows[,"source_node"]==i))
}
names(epi_d6_dna_size)=module_promoters_epi_d6



epi_d6_rna_size = NULL

for (i in module_transcripts_epi_d6){
  
  epi_d6_rna_size=c(epi_d6_rna_size,sum(epi_interaction_arrows[,"source_node"]==i))
}
names(epi_d6_rna_size)=module_transcripts_epi_d6


pe_d8_dna_size = NULL

for (i in module_promoters_pe_d8){
  pe_d8_dna_size=c(pe_d8_dna_size,sum(pe_interaction_arrows[,"source_node"]==i))
}
names(pe_d8_dna_size)=module_promoters_pe_d8


pe_d8_rna_size = NULL

for (i in module_transcripts_pe_d8){
  pe_d8_rna_size=c(pe_d8_rna_size,sum(pe_interaction_arrows[,"source_node"]==i))
}
names(pe_d8_rna_size)=module_transcripts_pe_d8


te_d6_dna_size = NULL

for (i in module_promoters_te_d6){
  te_d6_dna_size=c(te_d6_dna_size,sum(te_interaction_arrows[,"source_node"]==i))
}
names(te_d6_dna_size)=module_promoters_te_d6


te_d6_rna_size = NULL

for (i in module_transcripts_te_d6){
  te_d6_rna_size=c(te_d6_rna_size,sum(te_interaction_arrows[,"source_node"]==i))
}
names(te_d6_rna_size)=module_transcripts_te_d6

# save.image("2021_06_14_network.RData")
load("2021_06_14_network.RData")
epi_d6_dna_module_acp=ACP(ifelse(epi_d6_dna_adjacency_selected>median(epi_d6_dna_adjacency_selected),1,0))
epi_d6_rna_module_acp=ACP(ifelse(epi_d6_rna_adjacency_selected>median(epi_d6_rna_adjacency_selected),1,0))

pe_d8_dna_module_acp=ACP(ifelse(pe_d8_dna_adjacency_selected>median(pe_d8_dna_adjacency_selected),1,0))
pe_d8_rna_module_acp=ACP(ifelse(pe_d8_rna_adjacency_selected>median(pe_d8_rna_adjacency_selected),1,0))

te_d6_dna_module_acp=ACP(ifelse(te_d6_dna_adjacency_selected>median(te_d6_dna_adjacency_selected),1,0))
te_d6_rna_module_acp=ACP(ifelse(te_d6_rna_adjacency_selected>median(te_d6_rna_adjacency_selected),1,0))
epi_d6_module_3d_arrows=NULL

  
epi_3d_arrow_dna_dna=epi_d6_interaction_df[epi_d6_interaction_df[,"source_node"]%in%module_promoters_epi_d6 & epi_d6_interaction_df[,"target_node"]%in%module_promoters_epi_d6,c("source_node","target_node")]

xyz=NULL
for (i in epi_3d_arrow_dna_dna[,"source_node"]){
  xyz=rbind(xyz,epi_d6_dna_module_acp$x[rownames(epi_d6_dna_module_acp$x)==i,c(1:3)])  
}



xyz_end=NULL
for (i in epi_3d_arrow_dna_dna[,"target_node"]){
  xyz_end=rbind(xyz_end,epi_d6_dna_module_acp$x[rownames(epi_d6_dna_module_acp$x)==i,c(1:3)])  
}

epi_dna_dna_3d_arrows=data.frame(epi_3d_arrow_dna_dna,x_3d_arrow=xyz[,1],y_3d_arrow=xyz[,2], z_3d_arrow=xyz[,3],xend_3d_arrow=xyz_end[,1],yend_3d_arrow=xyz_end[,2],zend_3d_arrow=xyz_end[,3])





pe_d8_module_3d_arrows=NULL

  
pe_3d_arrow_dna_dna=pe_d8_interaction_df[pe_d8_interaction_df[,"source_node"]%in%module_promoters_pe_d8 & pe_d8_interaction_df[,"target_node"]%in%module_promoters_pe_d8,c("source_node","target_node")]

xyz=NULL
for (i in pe_3d_arrow_dna_dna[,"source_node"]){
  xyz=rbind(xyz,pe_d8_dna_module_acp$x[rownames(pe_d8_dna_module_acp$x)==i,c(1:3)])  
}



xyz_end=NULL
for (i in pe_3d_arrow_dna_dna[,"target_node"]){
  xyz_end=rbind(xyz_end,pe_d8_dna_module_acp$x[rownames(pe_d8_dna_module_acp$x)==i,c(1:3)])  
}

pe_dna_dna_3d_arrows=data.frame(pe_3d_arrow_dna_dna,x_3d_arrow=xyz[,1],y_3d_arrow=xyz[,2], z_3d_arrow=xyz[,3],xend_3d_arrow=xyz_end[,1],yend_3d_arrow=xyz_end[,2],zend_3d_arrow=xyz_end[,3])





te_d6_module_3d_arrows=NULL

  
te_3d_arrow_dna_dna=te_d6_interaction_df[te_d6_interaction_df[,"source_node"]%in%module_promoters_te_d6 & te_d6_interaction_df[,"target_node"]%in%module_promoters_te_d6,c("source_node","target_node")]

xyz=NULL
for (i in te_3d_arrow_dna_dna[,"source_node"]){
  xyz=rbind(xyz,te_d6_dna_module_acp$x[rownames(te_d6_dna_module_acp$x)==i,c(1:3)])  
}



xyz_end=NULL
for (i in te_3d_arrow_dna_dna[,"target_node"]){
  xyz_end=rbind(xyz_end,te_d6_dna_module_acp$x[rownames(te_d6_dna_module_acp$x)==i,c(1:3)])  
}

te_dna_dna_3d_arrows=data.frame(te_3d_arrow_dna_dna,x_3d_arrow=xyz[,1],y_3d_arrow=xyz[,2], z_3d_arrow=xyz[,3],xend_3d_arrow=xyz_end[,1],yend_3d_arrow=xyz_end[,2],zend_3d_arrow=xyz_end[,3])






epi_d6_module_3d_arrows=NULL

  
epi_3d_arrow_rna_rna=epi_d6_interaction_df[epi_d6_interaction_df[,"source_node"]%in%module_transcripts_epi_d6 & epi_d6_interaction_df[,"target_node"]%in%module_transcripts_epi_d6,c("source_node","target_node")]

xyz=NULL
for (i in epi_3d_arrow_rna_rna[,"source_node"]){
  xyz=rbind(xyz,epi_d6_rna_module_acp$x[rownames(epi_d6_rna_module_acp$x)==i,c(1:3)]+5)  
}



xyz_end=NULL
for (i in epi_3d_arrow_rna_rna[,"target_node"]){
  xyz_end=rbind(xyz_end,epi_d6_rna_module_acp$x[rownames(epi_d6_rna_module_acp$x)==i,c(1:3)]+5)  
}

epi_rna_rna_3d_arrows=data.frame(epi_3d_arrow_rna_rna,x_3d_arrow=xyz[,1],y_3d_arrow=xyz[,2], z_3d_arrow=xyz[,3],xend_3d_arrow=xyz_end[,1],yend_3d_arrow=xyz_end[,2],zend_3d_arrow=xyz_end[,3])





pe_d8_module_3d_arrows=NULL

  
pe_3d_arrow_rna_rna=pe_d8_interaction_df[pe_d8_interaction_df[,"source_node"]%in%module_transcripts_pe_d8 & pe_d8_interaction_df[,"target_node"]%in%module_transcripts_pe_d8,c("source_node","target_node")]

xyz=NULL
for (i in pe_3d_arrow_rna_rna[,"source_node"]){
  xyz=rbind(xyz,pe_d8_rna_module_acp$x[rownames(pe_d8_rna_module_acp$x)==i,c(1:3)]+5)  
}



xyz_end=NULL
for (i in pe_3d_arrow_rna_rna[,"target_node"]){
  xyz_end=rbind(xyz_end,pe_d8_rna_module_acp$x[rownames(pe_d8_rna_module_acp$x)==i,c(1:3)]+5)  
}

pe_rna_rna_3d_arrows=data.frame(pe_3d_arrow_rna_rna,x_3d_arrow=xyz[,1],y_3d_arrow=xyz[,2], z_3d_arrow=xyz[,3],xend_3d_arrow=xyz_end[,1],yend_3d_arrow=xyz_end[,2],zend_3d_arrow=xyz_end[,3])





te_d6_module_3d_arrows=NULL

  
te_3d_arrow_rna_rna=te_d6_interaction_df[te_d6_interaction_df[,"source_node"]%in%module_transcripts_te_d6 & te_d6_interaction_df[,"target_node"]%in%module_transcripts_te_d6,c("source_node","target_node")]

xyz=NULL
for (i in te_3d_arrow_rna_rna[,"source_node"]){
  xyz=rbind(xyz,te_d6_rna_module_acp$x[rownames(te_d6_rna_module_acp$x)==i,c(1:3)]+5)  
}



xyz_end=NULL
for (i in te_3d_arrow_rna_rna[,"target_node"]){
  xyz_end=rbind(xyz_end,te_d6_rna_module_acp$x[rownames(te_d6_rna_module_acp$x)==i,c(1:3)]+5)  
}

te_rna_rna_3d_arrows=data.frame(te_3d_arrow_rna_rna,x_3d_arrow=xyz[,1],y_3d_arrow=xyz[,2], z_3d_arrow=xyz[,3],xend_3d_arrow=xyz_end[,1],yend_3d_arrow=xyz_end[,2],zend_3d_arrow=xyz_end[,3])









epi_d6_module_3d_arrows=NULL

  
epi_3d_arrow_dna_rna=epi_d6_interaction_df[epi_d6_interaction_df[,"source_node"]%in%module_promoters_epi_d6 & epi_d6_interaction_df[,"target_node"]%in%module_transcripts_epi_d6,c("source_node","target_node")]

xyz=NULL
for (i in epi_3d_arrow_dna_rna[,"source_node"]){
  xyz=rbind(xyz,epi_d6_dna_module_acp$x[rownames(epi_d6_dna_module_acp$x)==i,c(1:3)])  
}



xyz_end=NULL
for (i in epi_3d_arrow_dna_rna[,"target_node"]){
  xyz_end=rbind(xyz_end,epi_d6_rna_module_acp$x[rownames(epi_d6_rna_module_acp$x)==i,c(1:3)]+5)  
}

epi_dna_rna_3d_arrows=data.frame(epi_3d_arrow_dna_rna,x_3d_arrow=xyz[,1],y_3d_arrow=xyz[,2], z_3d_arrow=xyz[,3],xend_3d_arrow=xyz_end[,1],yend_3d_arrow=xyz_end[,2],zend_3d_arrow=xyz_end[,3])





pe_d8_module_3d_arrows=NULL

  
pe_3d_arrow_dna_rna=pe_d8_interaction_df[pe_d8_interaction_df[,"source_node"]%in%module_promoters_pe_d8 & pe_d8_interaction_df[,"target_node"]%in%module_transcripts_pe_d8,c("source_node","target_node")]

xyz=NULL
for (i in pe_3d_arrow_dna_rna[,"source_node"]){
  xyz=rbind(xyz,pe_d8_dna_module_acp$x[rownames(pe_d8_dna_module_acp$x)==i,c(1:3)])  
}



xyz_end=NULL
for (i in pe_3d_arrow_dna_rna[,"target_node"]){
  xyz_end=rbind(xyz_end,pe_d8_rna_module_acp$x[rownames(pe_d8_rna_module_acp$x)==i,c(1:3)]+5)  
}

pe_dna_rna_3d_arrows=data.frame(pe_3d_arrow_dna_rna,x_3d_arrow=xyz[,1],y_3d_arrow=xyz[,2], z_3d_arrow=xyz[,3],xend_3d_arrow=xyz_end[,1],yend_3d_arrow=xyz_end[,2],zend_3d_arrow=xyz_end[,3])





te_d6_module_3d_arrows=NULL

  
te_3d_arrow_dna_rna=te_d6_interaction_df[te_d6_interaction_df[,"source_node"]%in%module_promoters_te_d6 & te_d6_interaction_df[,"target_node"]%in%module_transcripts_te_d6,c("source_node","target_node")]

xyz=NULL
for (i in te_3d_arrow_dna_rna[,"source_node"]){
  xyz=rbind(xyz,te_d6_dna_module_acp$x[rownames(te_d6_dna_module_acp$x)==i,c(1:3)])  
}



xyz_end=NULL
for (i in te_3d_arrow_dna_rna[,"target_node"]){
  xyz_end=rbind(xyz_end,te_d6_rna_module_acp$x[rownames(te_d6_rna_module_acp$x)==i,c(1:3)]+5)  
}

te_dna_rna_3d_arrows=data.frame(te_3d_arrow_dna_rna,x_3d_arrow=xyz[,1],y_3d_arrow=xyz[,2], z_3d_arrow=xyz[,3],xend_3d_arrow=xyz_end[,1],yend_3d_arrow=xyz_end[,2],zend_3d_arrow=xyz_end[,3])






epi_d6_module_3d_arrows=NULL

  
epi_3d_arrow_rna_dna=epi_d6_interaction_df[epi_d6_interaction_df[,"source_node"]%in%module_transcripts_epi_d6 & epi_d6_interaction_df[,"target_node"]%in%module_promoters_epi_d6,c("source_node","target_node")]

xyz=NULL
for (i in epi_3d_arrow_rna_dna[,"source_node"]){
  xyz=rbind(xyz,epi_d6_rna_module_acp$x[rownames(epi_d6_rna_module_acp$x)==i,c(1:3)]+5)  
}



xyz_end=NULL
for (i in epi_3d_arrow_rna_dna[,"target_node"]){
  xyz_end=rbind(xyz_end,epi_d6_dna_module_acp$x[rownames(epi_d6_dna_module_acp$x)==i,c(1:3)])  
}

epi_rna_dna_3d_arrows=data.frame(epi_3d_arrow_rna_dna,x_3d_arrow=xyz[,1],y_3d_arrow=xyz[,2], z_3d_arrow=xyz[,3],xend_3d_arrow=xyz_end[,1],yend_3d_arrow=xyz_end[,2],zend_3d_arrow=xyz_end[,3])





pe_d8_module_3d_arrows=NULL

  
pe_3d_arrow_rna_dna=pe_d8_interaction_df[pe_d8_interaction_df[,"source_node"]%in%module_transcripts_pe_d8 & pe_d8_interaction_df[,"target_node"]%in%module_promoters_pe_d8,c("source_node","target_node")]

xyz=NULL
for (i in pe_3d_arrow_rna_dna[,"source_node"]){
  xyz=rbind(xyz,pe_d8_rna_module_acp$x[rownames(pe_d8_rna_module_acp$x)==i,c(1:3)]+5)  
}



xyz_end=NULL
for (i in pe_3d_arrow_rna_dna[,"target_node"]){
  xyz_end=rbind(xyz_end,pe_d8_dna_module_acp$x[rownames(pe_d8_dna_module_acp$x)==i,c(1:3)])  
}

pe_rna_dna_3d_arrows=data.frame(pe_3d_arrow_rna_dna,x_3d_arrow=xyz[,1],y_3d_arrow=xyz[,2], z_3d_arrow=xyz[,3],xend_3d_arrow=xyz_end[,1],yend_3d_arrow=xyz_end[,2],zend_3d_arrow=xyz_end[,3])





te_d6_module_3d_arrows=NULL

  
te_3d_arrow_rna_dna=te_d6_interaction_df[te_d6_interaction_df[,"source_node"]%in%module_transcripts_te_d6 & te_d6_interaction_df[,"target_node"]%in%module_promoters_te_d6,c("source_node","target_node")]

xyz=NULL
for (i in te_3d_arrow_rna_dna[,"source_node"]){
  xyz=rbind(xyz,te_d6_rna_module_acp$x[rownames(te_d6_rna_module_acp$x)==i,c(1:3)]+5)  
}



xyz_end=NULL
for (i in te_3d_arrow_rna_dna[,"target_node"]){
  xyz_end=rbind(xyz_end,te_d6_dna_module_acp$x[rownames(te_d6_dna_module_acp$x)==i,c(1:3)])  
}

te_rna_dna_3d_arrows=data.frame(te_3d_arrow_rna_dna,x_3d_arrow=xyz[,1],y_3d_arrow=xyz[,2], z_3d_arrow=xyz[,3],xend_3d_arrow=xyz_end[,1],yend_3d_arrow=xyz_end[,2],zend_3d_arrow=xyz_end[,3])

epi_d6_3d_arrows=rbind(epi_dna_dna_3d_arrows, epi_dna_rna_3d_arrows, epi_rna_rna_3d_arrows, epi_rna_dna_3d_arrows)
pe_d8_3d_arrows=rbind(pe_dna_dna_3d_arrows, pe_dna_rna_3d_arrows, pe_rna_rna_3d_arrows, pe_rna_dna_3d_arrows)
te_d6_3d_arrows=rbind(te_dna_dna_3d_arrows, te_dna_rna_3d_arrows, te_rna_rna_3d_arrows, te_rna_dna_3d_arrows)
epi_d6_module_acp=rbind(epi_d6_dna_module_acp$x[,c(1:3)], epi_d6_rna_module_acp$x[,c(1:3)]+5)
epi_d6_size=c(epi_d6_dna_size,epi_d6_rna_size)

scene = list(
  xaxis = list(
  title = "",
  zeroline = FALSE,
  showline = FALSE,
  showticklabels = FALSE,
  showgrid = FALSE
),
  yaxis = list(
  title = "",
  zeroline = FALSE,
  showline = FALSE,
  showticklabels = FALSE,
  showgrid = FALSE
),
  zaxis = list(
  title = "",
  zeroline = FALSE,
  showline = FALSE,
  showticklabels = FALSE,
  showgrid = FALSE
))


epi_d6_module_acp_fig <- plot_ly(x=epi_d6_module_acp[,"PC1"], y=epi_d6_module_acp[,"PC2"], z=epi_d6_module_acp[,"PC3"]
,alpha=0.5,
size = ~log(epi_d6_size+1,10), marker = list(symbol = 'circle', sizemode = 'diameter'), sizes = c(13,120),color=~c(rep("dna",length(module_promoters_epi_d6)),rep("rna",length(module_transcripts_epi_d6))),colors=c("green","purple")
)  %>% add_markers(
              text = rownames(epi_d6_module_acp),
              textfont = list(size=log2(epi_d6_size+1.5)*15, color= c(rep("green",length(module_promoters_epi_d6)),rep("purple",length(module_transcripts_epi_d6)))),
              showlegend = T)%>%layout(scene=scene) 

epi_d6_module_arrow_fig=NULL

for (i in seq(nrow(rbind(epi_dna_dna_3d_arrows,epi_dna_rna_3d_arrows)))){
plot_ly() %>%
  add_trace(x = c(epi_d6_3d_arrows$x_3d_arrow[i],epi_d6_3d_arrows$xend_3d_arrow[i]),
    y = c(epi_d6_3d_arrows$y_3d_arrow[i],epi_d6_3d_arrows$yend_3d_arrow[i]),
    z = c(epi_d6_3d_arrows$z_3d_arrow[i],epi_d6_3d_arrows$zend_3d_arrow[i]),
    type = "scatter3d", mode = "lines", name = "lines", showlegend = FALSE,
    line=list(color="green", width = 1, alpha=0.1)) -> epi_d6_module_arrow_fig[[i]]
}


for (i in seq(nrow(rbind(epi_dna_dna_3d_arrows,epi_dna_rna_3d_arrows)),(nrow(rbind(epi_dna_dna_3d_arrows,epi_dna_rna_3d_arrows))+nrow(rbind(epi_rna_rna_3d_arrows,epi_rna_dna_3d_arrows))))){
plot_ly() %>%
  add_trace(x = c(epi_d6_3d_arrows$x_3d_arrow[i],epi_d6_3d_arrows$xend_3d_arrow[i]),
    y = c(epi_d6_3d_arrows$y_3d_arrow[i],epi_d6_3d_arrows$yend_3d_arrow[i]),
    z = c(epi_d6_3d_arrows$z_3d_arrow[i],epi_d6_3d_arrows$zend_3d_arrow[i]),
    type = "scatter3d", mode = "lines", name = "lines", showlegend = FALSE,
    line=list(color="purple", width = 1, alpha=0.1))%>%layout(scene=scene) -> epi_d6_module_arrow_fig[[i]]
}

epi_d6_module_arrow_fig$nodes=epi_d6_module_acp_fig

subplot(epi_d6_module_arrow_fig)
pe_d8_module_acp=rbind(pe_d8_dna_module_acp$x[,c(1:3)], pe_d8_rna_module_acp$x[,c(1:3)]+5)
pe_d8_size=c(pe_d8_dna_size,pe_d8_rna_size)

scene = list(
  xaxis = list(
  title = "",
  zeroline = FALSE,
  showline = FALSE,
  showticklabels = FALSE,
  showgrid = FALSE
),
  yaxis = list(
  title = "",
  zeroline = FALSE,
  showline = FALSE,
  showticklabels = FALSE,
  showgrid = FALSE
),
  zaxis = list(
  title = "",
  zeroline = FALSE,
  showline = FALSE,
  showticklabels = FALSE,
  showgrid = FALSE
))


pe_d8_module_acp_fig <- plot_ly(x=pe_d8_module_acp[,"PC1"], y=pe_d8_module_acp[,"PC2"], z=pe_d8_module_acp[,"PC3"]
,alpha=0.5,
size = ~log(pe_d8_size+1,10), marker = list(symbol = 'circle', sizemode = 'diameter'), sizes = c(13,120),color=~c(rep("dna",length(module_promoters_pe_d8)),rep("rna",length(module_transcripts_pe_d8))),colors=c("green","purple")
)  %>% add_markers(
              text = rownames(pe_d8_module_acp),
              textfont = list(size=log2(pe_d8_size+1.5)*15, color= c(rep("green",length(module_promoters_pe_d8)),rep("purple",length(module_transcripts_pe_d8)))),
              showlegend = T)%>%layout(scene=scene) 

pe_d8_module_arrow_fig=NULL

for (i in seq(nrow(rbind(pe_dna_dna_3d_arrows,pe_dna_rna_3d_arrows)))){
plot_ly() %>%
  add_trace(x = c(pe_d8_3d_arrows$x_3d_arrow[i],pe_d8_3d_arrows$xend_3d_arrow[i]),
    y = c(pe_d8_3d_arrows$y_3d_arrow[i],pe_d8_3d_arrows$yend_3d_arrow[i]),
    z = c(pe_d8_3d_arrows$z_3d_arrow[i],pe_d8_3d_arrows$zend_3d_arrow[i]),
    type = "scatter3d", mode = "lines", name = "lines", showlegend = FALSE,
    line=list(color="green", width = 1, alpha=0.1)) -> pe_d8_module_arrow_fig[[i]]
}


for (i in seq(nrow(rbind(pe_dna_dna_3d_arrows,pe_dna_rna_3d_arrows)),(nrow(rbind(pe_dna_dna_3d_arrows,pe_dna_rna_3d_arrows))+nrow(rbind(pe_rna_rna_3d_arrows,pe_rna_dna_3d_arrows))))){
plot_ly() %>%
  add_trace(x = c(pe_d8_3d_arrows$x_3d_arrow[i],pe_d8_3d_arrows$xend_3d_arrow[i]),
    y = c(pe_d8_3d_arrows$y_3d_arrow[i],pe_d8_3d_arrows$yend_3d_arrow[i]),
    z = c(pe_d8_3d_arrows$z_3d_arrow[i],pe_d8_3d_arrows$zend_3d_arrow[i]),
    type = "scatter3d", mode = "lines", name = "lines", showlegend = FALSE,
    line=list(color="purple", width = 1, alpha=0.1))%>%layout(scene=scene) -> pe_d8_module_arrow_fig[[i]]
}

pe_d8_module_arrow_fig$nodes=pe_d8_module_acp_fig

subplot(pe_d8_module_arrow_fig)
te_d6_module_acp=rbind(te_d6_dna_module_acp$x[,c(1:3)], te_d6_rna_module_acp$x[,c(1:3)]+5)
te_d6_size=c(te_d6_dna_size,te_d6_rna_size)

scene = list(
  xaxis = list(
  title = "",
  zeroline = FALSE,
  showline = FALSE,
  showticklabels = FALSE,
  showgrid = FALSE
),
  yaxis = list(
  title = "",
  zeroline = FALSE,
  showline = FALSE,
  showticklabels = FALSE,
  showgrid = FALSE
),
  zaxis = list(
  title = "",
  zeroline = FALSE,
  showline = FALSE,
  showticklabels = FALSE,
  showgrid = FALSE
))


te_d6_module_acp_fig <- plot_ly(x=te_d6_module_acp[,"PC1"], y=te_d6_module_acp[,"PC2"], z=te_d6_module_acp[,"PC3"]
,alpha=0.5,
size = ~log(te_d6_size+1,10), marker = list(symbol = 'circle', sizemode = 'diameter'), sizes = c(13,120),color=~c(rep("dna",length(module_promoters_te_d6)),rep("rna",length(module_transcripts_te_d6))),colors=c("green","purple")
)  %>% add_markers(
              text = rownames(te_d6_module_acp),
              textfont = list(size=log2(te_d6_size+1.5)*15, color= c(rep("green",length(module_promoters_te_d6)),rep("purple",length(module_transcripts_te_d6)))),
              showlegend = T)%>%layout(scene=scene) 

te_d6_module_arrow_fig=NULL

for (i in seq(nrow(rbind(te_dna_dna_3d_arrows,te_dna_rna_3d_arrows)))){
plot_ly() %>%
  add_trace(x = c(te_d6_3d_arrows$x_3d_arrow[i],te_d6_3d_arrows$xend_3d_arrow[i]),
    y = c(te_d6_3d_arrows$y_3d_arrow[i],te_d6_3d_arrows$yend_3d_arrow[i]),
    z = c(te_d6_3d_arrows$z_3d_arrow[i],te_d6_3d_arrows$zend_3d_arrow[i]),
    type = "scatter3d", mode = "lines", name = "lines", showlegend = FALSE,
    line=list(color="green", width = 1, alpha=0.1)) -> te_d6_module_arrow_fig[[i]]
}


for (i in seq(nrow(rbind(te_dna_dna_3d_arrows,te_dna_rna_3d_arrows)),(nrow(rbind(te_dna_dna_3d_arrows,te_dna_rna_3d_arrows))+nrow(rbind(te_rna_rna_3d_arrows,te_rna_dna_3d_arrows))))){
plot_ly() %>%
  add_trace(x = c(te_d6_3d_arrows$x_3d_arrow[i],te_d6_3d_arrows$xend_3d_arrow[i]),
    y = c(te_d6_3d_arrows$y_3d_arrow[i],te_d6_3d_arrows$yend_3d_arrow[i]),
    z = c(te_d6_3d_arrows$z_3d_arrow[i],te_d6_3d_arrows$zend_3d_arrow[i]),
    type = "scatter3d", mode = "lines", name = "lines", showlegend = FALSE,
    line=list(color="purple", width = 1, alpha=0.1))%>%layout(scene=scene) -> te_d6_module_arrow_fig[[i]]
}

te_d6_module_arrow_fig$nodes=te_d6_module_acp_fig

subplot(te_d6_module_arrow_fig)

Mix-Kernel analysis

We want now to go further and use a kernel based approach, which computes a multi-dimension space in which embryo cells will place given their vicinity across gene promoter methylation and gene expression. This exploratory non-supervised analysis traces a non-linear frontier of decision that allocates individuals into groups (Mariette and Vialaneix, 2018).

[1] 130 130
[1] 130 130
cim.kernel(dna = dna_kernel,
           rna = rna_kernel,
           method = "square"
)

Variance explained by kernel for the two datasets

<center>**Variance explained by kernel for the two datasets**</center>
rna_dna_meta_kernel <- combine.kernels(dna = dna_kernel, 
                                       rna = rna_kernel,
                                       method = "full-UMKL")

kernel.pca.result <- kernel.pca(rna_dna_meta_kernel, ncomp = 10)

# save.image("2021_06_09_embryo_kernel")
load("2021_06_09_embryo_kernel")

We needed a space within cells segregate by developmental stage and lineage progression -> kernel almost succeeded. Indeed, contrary to single PCA or UMAP, kernel recapitulates both developmental stage progression and lineage specification.

We can use this kernel space to visualize methylation and expression of genes, notably WGCNA module genes, throughout early development of the human embryo.

lineage_kernel=lineage[rownames(kernel.pca.result$x)]
next3d()
scene_kernel = list(camera = list(eye = list(x = 0, y = 0, z = 1.25)))
fig <- plot_ly(x=kernel.pca.result$x[,1],y=kernel.pca.result$x[,2],z=kernel.pca.result$x[,3],color = ~factor(lineage_kernel,levels=c("Epi", "PE", "TE")), colors = c("red","green","blue"),alpha=0.6)
fig <- fig %>% add_markers()
fig

3D Kernel PCA (KPCA)

day_kernel=day[rownames(kernel.pca.result$x)]
next3d()
scene_kernel = list(camera = list(eye = list(x = 0, y = 0, z = 1.25)))
fig <- plot_ly(x=kernel.pca.result$x[,1],y=kernel.pca.result$x[,2],z=kernel.pca.result$x[,3],color = ~factor(day_kernel,levels=c("D6", "D8", "D10", "D12")), colors = c("yellow","turquoise","purple","red"),alpha=0.6)
fig <- fig %>% add_markers()
fig

3D Kernel PCA (KPCA)

promoter_kernel=log2(dna_upm_subset[which(rownames(dna_upm_subset)=="POU5F1"),]+1)
next3d()
scene_kernel = list(camera = list(eye = list(x = 0, y = 0, z = 1.25)))
fig <- plot_ly(x=kernel.pca.result$x[,1],y=kernel.pca.result$x[,2],z=kernel.pca.result$x[,3],color = ~promoter_kernel,alpha=1, colors=c("blue","lightblue","#FFC0CB","red"))
fig <- fig %>% add_markers()
fig

3D Kernel PCA (KPCA)

transcript_kernel=log2(rna_subset[which(rownames(rna_subset)=="POU5F1"),]+1)
next3d()
scene_kernel = list(camera = list(eye = list(x = 0, y = 0, z = 1.25)))
fig <- plot_ly(x=kernel.pca.result$x[,1],y=kernel.pca.result$x[,2],z=kernel.pca.result$x[,3],color = ~transcript_kernel,alpha=1, colors=c("blue","lightblue","#FFC0CB","red"))
fig <- fig %>% add_markers()
fig

3D Kernel PCA (KPCA)

terf1_rna_boxplot=data.frame(metadata,expr=log2(rna_subset[which(rownames(rna_subset)=="TERF1"),]+1),lineage_day=paste0(metadata$lineage,"_",metadata$day))


terf1_rna_boxplot$lineage_day=factor(terf1_rna_boxplot$lineage_day,levels=c("Epi_D6", "Epi_D8", "Epi_D10", "PE_D6", "PE_D8", "PE_D10", "PE_D12", "TE_D6", "TE_D8", "TE_D10", "TE_D12"))

ggplot(terf1_rna_boxplot,aes(x=lineage_day,y=expr,fill=lineage_day))+
  geom_boxplot()+
  scale_fill_manual(values=c("#ff6eeb","#f01dbd","#f01d8d","#5df700","#20cc2d","#10ae4a","#39a455","#04f1fc","#20c1e1","blue","darkblue"))+
  labs(title="TERF1 gene expression")

CHEK2_dna_boxplot=data.frame(metadata,expr=log2(dna_upm_subset[which(rownames(dna_upm_subset)=="CHEK2"),]+1),lineage_day=paste0(metadata$lineage,"_",metadata$day))

CHEK2_dna_boxplot$lineage_day=factor(CHEK2_dna_boxplot$lineage_day,levels=c("Epi_D6", "Epi_D8", "Epi_D10", "PE_D6", "PE_D8", "PE_D10", "PE_D12", "TE_D6", "TE_D8", "TE_D10", "TE_D12"))

ggplot(CHEK2_dna_boxplot,aes(x=lineage_day,y=expr,fill=lineage_day))+
  geom_boxplot()+
  scale_fill_manual(values=c("#ff6eeb","#f01dbd","#f01d8d","#5df700","#20cc2d","#10ae4a","#39a455","#04f1fc","#20c1e1","blue","darkblue"))+
  labs(title="CHEK2 DNA methylation")

CHEK2_rna_boxplot=data.frame(metadata,expr=log2(rna_subset[which(rownames(rna_subset)=="CHEK2"),]+1),lineage_day=paste0(metadata$lineage,"_",metadata$day))

CHEK2_rna_boxplot$lineage_day=factor(CHEK2_rna_boxplot$lineage_day,levels=c("Epi_D6", "Epi_D8", "Epi_D10", "PE_D6", "PE_D8", "PE_D10", "PE_D12", "TE_D6", "TE_D8", "TE_D10", "TE_D12"))

ggplot(CHEK2_rna_boxplot,aes(x=lineage_day,y=expr,fill=lineage_day))+
  geom_boxplot()+
  scale_fill_manual(values=c("#ff6eeb","#f01dbd","#f01d8d","#5df700","#20cc2d","#10ae4a","#39a455","#04f1fc","#20c1e1","blue","darkblue"))+
  labs(title="CHEK2 gene expression")

<center>**3D Kernel PCA (KPCA)**</center><center>**3D Kernel PCA (KPCA)**</center><center>**3D Kernel PCA (KPCA)**</center>

plot(kernel.pca.result)

Variance explained by PC of KPCA

<center>**Variance explained by PC of KPCA**</center>

Clearly, the kernel PCA (KPCA) shows a space in which embryonic cell vicinity recapitulates both lineages and developmental day. This is of utmost interest as we could not combine these two traits with sufficient resolution.

Therefore, the kernel based approach integrates gene promoter methylation with gene expression and computes a space recapitulating developmental progression and lineage specification of the early human embryo.

PERSPECTIVE : Machine learning -> algorithm that can assign robustly cell lineages and developmental time

We can use this model to build an algorithm that assign a given embryo cell to lineage and developmental stage, provided DNA methylation and gene expression measures.

Machine learning model:

  • create
  • train
  • test (on other datasets too, should work for single dataset and both dataset input)
  • use

A predictive model could help to resolve inter-embryo developmental delays or misleaded annotation, and thus accurate developmental stage and lineage maturation identification. This also could help to unify the diverse datasets on a common annotation, thus helping to compare these data.

For this purpose, we could subset the current dataset and use the kernel space and the WGCNA modules, to train the model on one set of cells, and test it on the other set, both of which representative of developmental day and lineage distribution.

However, it would be better that once new datasets are produced, we test our model on these.

Difficulties

  • no access to fastq files
  • difficulties to normalise data properly
  • results strongly depend on input data distribution
  • hard to select appropriate soft-power threshold for WGCNA
  • functional enrichment databases seem inaccurate for the human embryo

Session info

sessionInfo()
R version 4.0.3 (2020-10-10)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19042)

Matrix products: default

locale:
[1] LC_COLLATE=English_United Kingdom.1252 
[2] LC_CTYPE=English_United Kingdom.1252   
[3] LC_MONETARY=English_United Kingdom.1252
[4] LC_NUMERIC=C                           
[5] LC_TIME=English_United Kingdom.1252    

attached base packages:
 [1] grid      parallel  stats4    stats     graphics  grDevices utils    
 [8] datasets  methods   base     

other attached packages:
 [1] scales_1.1.1                networkly_0.2              
 [3] ggnetwork_0.5.9             sna_2.6                    
 [5] network_1.17.0              statnet.common_4.5.0       
 [7] igraph_1.2.6                intergraph_2.0-2           
 [9] gprofiler2_0.2.0            mixKernel_0.6              
[11] reticulate_1.20             mixOmics_6.14.1            
[13] lattice_0.20-41             MASS_7.3-53.1              
[15] dendextend_1.15.1           ComplexHeatmap_2.6.2       
[17] factoextra_1.0.7            edgeR_3.32.1               
[19] limma_3.46.0                dplyr_1.0.6                
[21] plotly_4.9.3                WGCNA_1.70-3               
[23] fastcluster_1.1.25          dynamicTreeCut_1.63-1      
[25] FactoMineR_2.4              doParallel_1.0.16          
[27] iterators_1.0.13            foreach_1.5.1              
[29] biomaRt_2.46.3              MOFAdata_1.3.1             
[31] MOFA_1.6.2                  DESeq2_1.30.1              
[33] SummarizedExperiment_1.20.0 Biobase_2.50.0             
[35] MatrixGenerics_1.2.1        GenomicRanges_1.42.0       
[37] GenomeInfoDb_1.26.7         IRanges_2.24.1             
[39] S4Vectors_0.28.1            BiocGenerics_0.36.1        
[41] stringr_1.4.0               ggplot2_3.3.3              
[43] uwot_0.1.10                 Matrix_1.2-18              
[45] matrixStats_0.58.0          data.table_1.14.0          
[47] rgl_0.106.8                 knitr_1.33                 

loaded via a namespace (and not attached):
  [1] rappdirs_0.3.3              coda_0.19-4                
  [3] tidyr_1.1.3                 bit64_4.0.5                
  [5] DelayedArray_0.16.3         rpart_4.1-15               
  [7] RCurl_1.98-1.3              generics_0.1.0             
  [9] preprocessCore_1.52.1       cowplot_1.1.1              
 [11] RSQLite_2.2.7               bit_4.0.4                  
 [13] webshot_0.5.2               xml2_1.3.2                 
 [15] httpuv_1.6.1                assertthat_0.2.1           
 [17] viridis_0.6.1               xfun_0.22                  
 [19] hms_1.1.0                   jquerylib_0.1.4            
 [21] evaluate_0.14               promises_1.2.0.1           
 [23] fansi_0.4.2                 progress_1.2.2             
 [25] dbplyr_2.1.1                DBI_1.1.1                  
 [27] geneplotter_1.68.0          tmvnsim_1.0-2              
 [29] htmlwidgets_1.5.3           rARPACK_0.11-0             
 [31] purrr_0.3.4                 ellipsis_0.3.2             
 [33] corrplot_0.88               RSpectra_0.16-0            
 [35] crosstalk_1.1.1             backports_1.2.1            
 [37] permute_0.9-5               annotate_1.68.0            
 [39] vctrs_0.3.8                 Cairo_1.5-12.2             
 [41] cachem_1.0.4                withr_2.4.2                
 [43] checkmate_2.0.0             vegan_2.5-7                
 [45] prettyunits_1.1.1           MultiAssayExperiment_1.16.0
 [47] mnormt_2.0.2                cluster_2.1.2              
 [49] ape_5.5                     lazyeval_0.2.2             
 [51] crayon_1.4.1                ellipse_0.4.2              
 [53] genefilter_1.72.1           labeling_0.4.2             
 [55] pkgconfig_2.0.3             nlme_3.1-152               
 [57] vipor_0.4.5                 nnet_7.3-15                
 [59] rlang_0.4.10                lifecycle_1.0.0            
 [61] miniUI_0.1.1.1              extrafontdb_1.0            
 [63] BiocFileCache_1.14.0        phyloseq_1.34.0            
 [65] Rhdf5lib_1.12.1             base64enc_0.1-3            
 [67] beeswarm_0.3.1              GlobalOptions_0.1.2        
 [69] pheatmap_1.0.12             png_0.1-7                  
 [71] viridisLite_0.4.0           rjson_0.2.20               
 [73] bitops_1.0-7                rhdf5filters_1.2.1         
 [75] Biostrings_2.58.0           blob_1.2.1                 
 [77] shape_1.4.6                 manipulateWidget_0.10.1    
 [79] jpeg_0.1-8.1                leaps_3.1                  
 [81] memoise_2.0.0               magrittr_2.0.1             
 [83] plyr_1.8.6                  zlibbioc_1.36.0            
 [85] compiler_4.0.3              RColorBrewer_1.1-2         
 [87] clue_0.3-59                 ade4_1.7-16                
 [89] XVector_0.30.0              htmlTable_2.2.1            
 [91] Formula_1.2-4               mgcv_1.8-35                
 [93] tidyselect_1.1.1            stringi_1.5.3              
 [95] highr_0.9                   yaml_2.2.1                 
 [97] askpass_1.1                 locfit_1.5-9.4             
 [99] latticeExtra_0.6-29         ggrepel_0.9.1              
[101] sass_0.4.0                  tools_4.0.3                
[103] circlize_0.4.12             rstudioapi_0.13            
[105] foreign_0.8-81              gridExtra_2.3              
[107] farver_2.1.0                scatterplot3d_0.3-41       
[109] digest_0.6.27               FNN_1.1.3                  
[111] shiny_1.6.0                 quadprog_1.5-8             
[113] Rcpp_1.0.6                  later_1.2.0                
[115] RcppAnnoy_0.0.18            httr_1.4.2                 
[117] AnnotationDbi_1.52.0        psych_2.1.3                
[119] colorspace_2.0-1            LDRTools_0.2-1             
[121] XML_3.99-0.6                fs_1.5.0                   
[123] splines_4.0.3               pkgdown_1.6.1              
[125] multtest_2.46.0             xtable_1.8-4               
[127] jsonlite_1.7.2              corpcor_1.6.9              
[129] flashClust_1.01-2           R6_2.5.0                   
[131] Hmisc_4.5-0                 pillar_1.6.1               
[133] htmltools_0.5.1.1           mime_0.10                  
[135] glue_1.4.2                  fastmap_1.1.0              
[137] DT_0.18                     BiocParallel_1.24.1        
[139] codetools_0.2-18            utf8_1.2.1                 
[141] bslib_0.2.5.1               tibble_3.1.1               
[143] curl_4.3.1                  ggbeeswarm_0.6.0           
[145] magick_2.7.2                GO.db_3.12.1               
[147] openssl_1.4.4               Rttf2pt1_1.3.8             
[149] survival_3.2-10             rmarkdown_2.8              
[151] biomformat_1.18.0           munsell_0.5.0              
[153] GetoptLong_1.0.5            rhdf5_2.34.0               
[155] GenomeInfoDbData_1.2.4      impute_1.64.0              
[157] reshape2_1.4.4              gtable_0.3.0               
[159] extrafont_0.17             

References

Argelaguet, R., Velten, B., Arnol, D., Dietrich, S., Zenz, T., Marioni, J. C., Buettner, F., Huber, W., & Stegle, O. (2018). Multi-Omics Factor Analysis-a framework for unsupervised integration of multi-omics data sets. Molecular systems biology, 14(6), e8124. https://doi.org/10.15252/msb.20178124
Jaskowiak, P. A., Campello, R. J., & Costa, I. G. (2014). On the selection of appropriate distances for gene expression data clustering. BMC bioinformatics, 15 Suppl 2(Suppl 2), S2. https://doi.org/10.1186/1471-2105-15-S2-S2
Kobak, D., Linderman, G.C. Initialization is critical for preserving global data structure in both t-SNE and UMAP. Nat Biotechnol 39, 156–157 (2021). https://doi-org.proxy.insermbiblio.inist.fr/10.1038/s41587-020-00809-z
Langfelder, P., Horvath, S. WGCNA: an R package for weighted correlation network analysis. BMC Bioinformatics 9, 559 (2008). https://doi.org/10.1186/1471-2105-9-559
Lawlor, N., Fabbri, A., Guan, P., George, J., & Karuturi, R. K. (2016). multiClust: An R-package for Identifying Biologically Relevant Clusters in Cancer Transcriptome Profiles. Cancer informatics, 15, 103–114. https://doi.org/10.4137/CIN.S38000
Lever, J., Krzywinski, M. & Altman, N. Principal component analysis. Nat Methods 14, 641–642 (2017). https://doi.org/10.1038/nmeth.4346
Mariette, J., & Villa-Vialaneix, N. (2018). Unsupervised multiple kernel learning for heterogeneous data integration. Bioinformatics (Oxford, England), 34(6), 1009–1015.https://doi.org/10.1093/bioinformatics/btx682
MolĆØ, M. A., Weberling, A., & Zernicka-Goetz, M. (2020). Comparative analysis of human and mouse development: From zygote to pre-gastrulation. Current topics in developmental biology, 136, 113–138. https://doi.org/10.1016/bs.ctdb.2019.10.002
Zhou, F., Wang, R., Yuan, P., Ren, Y., Mao, Y., Li, R., Lian, Y., Li, J., Wen, L., Yan, L., Qiao, J., & Tang, F. (2019). Reconstituting the transcriptome and DNA methylome landscapes of human implantation. Nature, 572(7771), 660–664. https://doi.org/10.1038/s41586-019-1500-0
Zoppi, J., Guillaume, JF., Neunlist, M. et al.Ā MiBiOmics: an interactive web application for multi-omics data exploration and integration. BMC Bioinformatics 22, 6 (2021). https://doi.org/10.1186/s12859-020-03921-8
LS0tDQp0aXRsZTogIkludGVncmF0ZWQgYW5hbHlzaXMgb2YgdGhlIGVhcmx5IGh1bWFuIGVtYnJ5b1xuZnJvbSBtdWx0aS1vbWljcyBzaW5nbC1jZWxsIGRhdGEiDQphdXRob3I6ICJHYcOrbCBDQVNURUwiDQpkYXRlOiAnYHIgU3lzLkRhdGUoKWAnDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAgaGlnaGxpZ2h0OiB6ZW5idXJuDQogICAgdGhlbWU6IGNlcnVsZWFuDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIG1heC13aWR0aDogMTgwMHB4DQogICAgbWFyZ2luLWxlZnQ6IGF1dG8NCiAgICBtYXJnaW4tcmlnaHQ6IGF1dG8NCiAgICB3aWRlc2NyZWVuOiB5ZXMNCiAgaW9zbGlkZXNfcHJlc2VudGF0aW9uOg0KICAgIHNsaWRlX2xldmVsOiAyDQogICAgc2VsZl9jb250YWluZWQ6IHllcw0KICAgIGNvbG9ydGhlbWU6IGRvbHBoaW4NCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAgZmlnX2hlaWdodDogNQ0KICAgIGZpZ193aWR0aDogNw0KICAgIGZvbnR0aGVtZTogc3RydWN0dXJlYm9sZA0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgICBzbWFsbGVyOiB5ZXMNCiAgICB0b2M6IHllcw0KICAgIHdpZGVzY3JlZW46IHllcw0KICBwZGZfZG9jdW1lbnQ6DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIGhpZ2hsaWdodDogemVuYnVybg0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiAzDQogIGJlYW1lcl9wcmVzZW50YXRpb246DQogICAgY29sb3J0aGVtZTogZG9scGhpbg0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBmaWdfaGVpZ2h0OiA1DQogICAgZmlnX3dpZHRoOiA3DQogICAgZm9udHRoZW1lOiBzdHJ1Y3R1cmVib2xkDQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICAgIGluY3JlbWVudGFsOiBubw0KICAgIGtlZXBfdGV4OiBubw0KICAgIHNsaWRlX2xldmVsOiAyDQogICAgdGhlbWU6IE1vbnRwZWxsaWVyDQogICAgdG9jOiB5ZXMNCiAgcmV2ZWFsanM6OnJldmVhbGpzX3ByZXNlbnRhdGlvbjoNCiAgICB0aGVtZTogbmlnaHQNCiAgICB0cmFuc2l0aW9uOiBub25lDQogICAgc2VsZl9jb250YWluZWQ6IHRydWUNCiAgICBjc3M6IC4uL3NsaWRlcy5jc3MNCiAgc2xpZHlfcHJlc2VudGF0aW9uOg0KICAgIHNtYXJ0OiBubw0KICAgIHNsaWRlX2xldmVsOiAyDQogICAgc2VsZl9jb250YWluZWQ6IHllcw0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBmaWdfaGVpZ2h0OiA1DQogICAgZmlnX3dpZHRoOiA3DQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICAgIGluY3JlbWVudGFsOiBubw0KICAgIGtlZXBfbWQ6IHllcw0KICAgIHNtYWxsZXI6IHllcw0KICAgIHRoZW1lOiBjZXJ1bGVhbg0KICAgIHRvYzogeWVzDQogICAgd2lkZXNjcmVlbjogeWVzDQogIHBvd2VycG9pbnRfcHJlc2VudGF0aW9uOg0KICAgIHNsaWRlX2xldmVsOiAyDQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIGZpZ19oZWlnaHQ6IDUNCiAgICBmaWdfd2lkdGg6IDcNCiAgICB0b2M6IHllcw0KZm9udC1pbXBvcnQ6IGh0dHA6Ly9mb250cy5nb29nbGVhcGlzLmNvbS9jc3M/ZmFtaWx5PVJpc3F1ZQ0KZm9udC1mYW1pbHk6IEdhcmFtb25kDQp0cmFuc2l0aW9uOiBsaW5lYXINCmVkaXRvcl9vcHRpb25zOiANCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGNvbnNvbGUNCmhlYWRlci1pbmNsdWRlczoNCiAgIC0gXHVzZXBhY2thZ2V7ZmxvYXRyb3d9DQogICAtIFxmbG9hdHNldHVwW2ZpZ3VyZV17Y2FwcG9zaXRpb249dG9wfQ0KLS0tDQoNCjxzdHlsZT4NCi5tYWluLWNvbnRhaW5lciB7DQogICAgbWluLXdpZHRoOiAxODAwcHg7DQogICAgbWFyZ2luLWxlZnQ6IDBweDsNCiAgICBtYXJnaW4tcmlnaHQ6IDBweDsNCn0NCjwvc3R5bGU+DQoNCmBgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCnNldHdkKCJDOi9Vc2Vycy9FMTM3ODMzVC9EZXNrdG9wL2R1YmlpL3N0YWdlL2R1YmlpX3N0YWdlLyIpDQpzb3VyY2UoImh0dHBzOi8vZ2l0bGFiLnVuaXYtbmFudGVzLmZyL0UxMTQ0MjRaL3ZlbmVSL3Jhdy9tYXN0ZXIvbG9hZEZ1bi5SP2lubGluZT1mYWxzZSIpDQoNCiMgaW5zdGFsbC5wYWNrYWdlcygiaW50ZXJncmFwaCIsZGVwZW5kZW5jaWVzID0gVCwgZm9yY2U9VCxJTlNUQUxMX29wdHMgPSAnLS1uby1sb2NrJykgIyB0aGlzIHN5bnRheCB3b3JrcyBvbiB0aGUgUnN0dWRpbyBjbHVzdGVyDQoNCiMgQmlvY01hbmFnZXI6Omluc3RhbGwoInNuYSIsZGVwZW5kZW5jaWVzID0gVCwgZm9yY2U9VCxJTlNUQUxMX29wdHMgPSAnLS1uby1sb2NrJykgIyBpdCB3b3JrZWQgISBwdXQgbm8gZm9yIG1hbmlwdWxhdGV3aWRnZXQNCg0KIyByZW1vdGVzOjppbnN0YWxsX2dpdGh1YigiZGdyYXBvdi9uZXR3b3JrbHkiKQ0KDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShyZ2wpDQpsaWJyYXJ5KGRhdGEudGFibGUpDQpsaWJyYXJ5KG1hdHJpeFN0YXRzKQ0KbGlicmFyeSh1d290KQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShzdHJpbmdyKQ0KbGlicmFyeShTNFZlY3RvcnMpDQpsaWJyYXJ5KERFU2VxMikNCmxpYnJhcnkoTU9GQSkNCmxpYnJhcnkoTU9GQWRhdGEpDQpsaWJyYXJ5KEJpb2Jhc2UpDQpsaWJyYXJ5KEJpb2NHZW5lcmljcykNCmxpYnJhcnkoYmlvbWFSdCkNCmxpYnJhcnkoZm9yZWFjaCkNCmxpYnJhcnkoZG9QYXJhbGxlbCkNCmxpYnJhcnkoRmFjdG9NaW5lUikNCmxpYnJhcnkoV0dDTkEpDQpsaWJyYXJ5KHJnbCkNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZWRnZVIpDQpsaWJyYXJ5KGZhY3RvZXh0cmEpDQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQ0KbGlicmFyeShkZW5kZXh0ZW5kKQ0KbGlicmFyeShtaXhLZXJuZWwpDQpsaWJyYXJ5KGdwcm9maWxlcjIpDQpsaWJyYXJ5KGludGVyZ3JhcGgpDQpsaWJyYXJ5KGlncmFwaCkNCmxpYnJhcnkoc25hKQ0KbGlicmFyeShnZ25ldHdvcmspDQpsaWJyYXJ5KG5ldHdvcmtseSkNCmxpYnJhcnkoc2NhbGVzKQ0KDQoNCmZhc3RSZWFkMiA8LSBmdW5jdGlvbihmaWxlTmFtZSwgc2VwID0gJ1x0Jyxyb3cubmFtZXMgPSAxLHJtLmNvbHVtbnM9MCxhcy5tYXRyaXg9RkFMU0Usc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSwuLi4pew0KICByZXF1aXJlKGRhdGEudGFibGUpDQogIGRhdCA8LSBhcy5kYXRhLmZyYW1lKGRhdGEudGFibGU6OmZyZWFkKGZpbGVOYW1lLHN0cmluZ3NBc0ZhY3RvcnM9c3RyaW5nc0FzRmFjdG9ycywgc2VwID0gc2VwLC4uLikpDQogIGlmKCFpcy5udWxsKHJvdy5uYW1lcykpew0KICAgIHJvd25hbWVzKGRhdCkgPC0gbWFrZS5uYW1lcyhkYXRbLHJvdy5uYW1lc10sIHVuaXF1ZSA9IFRSVUUpDQogICAgZGF0IDwtIGRhdFssYygtcm93Lm5hbWVzLC1ybS5jb2x1bW5zKSxkcm9wPUZBTFNFXQ0KICB9DQogIGlmKGFzLm1hdHJpeCkgZGF0PC1hcy5tYXRyaXgoZGF0KQ0KICByZXR1cm4oZGF0KQ0KfQ0KDQp0aW1lTm93PC1mdW5jdGlvbih4PU5VTEwsIHk9TlVMTCwgbT1OVUxMLCBkPU5VTEwgLCBoPU5VTEwsIG1pbj1OVUxMLHltZF9oX209TlVMTCl7eD1kYXRlKCk7DQp5PXN0cl9zdWIoeCwtNCwtMSkNCmlmKHN0cl9zdWIoeCwtMjAsLTE4KT09IkphbiIpe209IjAxIn0NCmlmKHN0cl9zdWIoeCwtMjAsLTE4KT09IkZlYiIpe209IjAyIn0NCmlmKHN0cl9zdWIoeCwtMjAsLTE4KT09Ik1hciIpe209IjAzIn0NCmlmKHN0cl9zdWIoeCwtMjAsLTE4KT09IkFwciIpe209IjA0In0NCmlmKHN0cl9zdWIoeCwtMjAsLTE4KT09Ik1heSIpe209IjA1In0NCmlmKHN0cl9zdWIoeCwtMjAsLTE4KT09Ikp1biIpe209IjA2In0NCmlmKHN0cl9zdWIoeCwtMjAsLTE4KT09Ikp1bCIpe209IjA3In0NCmlmKHN0cl9zdWIoeCwtMjAsLTE4KT09IkF1ZyIpe209IjA4In0gICAgICAgICAgICANCmlmKHN0cl9zdWIoeCwtMjAsLTE4KT09IlNlcCIpe209IjA5In0NCmlmKHN0cl9zdWIoeCwtMjAsLTE4KT09Ik9jdCIpe209IjEwIn0NCmlmKHN0cl9zdWIoeCwtMjAsLTE4KT09Ik5vdiIpe209IjExIn0NCmlmKHN0cl9zdWIoeCwtMjAsLTE4KT09IkRlYyIpe209IjEyIn0NCmQ9aWZlbHNlKHN0cl9zdWIoeCwtMTYsLTE2KSA9PSIgIixwYXN0ZTAoIjAiLHN0cl9zdWIoeCwtMTUsLTE1KSksc3RyX3N1Yih4LC0xNiwtMTUpKQ0KaD1zdHJfc3ViKHgsLTEzLC0xMikNCm1pbj1zdHJfc3ViKHgsLTEwLC05KQ0KDQpyZXR1cm4oeW1kX2hfbWluPXBhc3RlKHNlcD0iIix5LCJfIixtLCJfIixkLCJfIixoLCJfIixtaW4pKQ0KfQ0KDQprbml0cjo6b3B0c19jaHVuayRzZXQoDQogIGVjaG8gPSBUUlVFLCANCiAgZXZhbCA9IFRSVUUsIA0KICB3YXJuaW5nID0gRkFMU0UsIA0KICBtZXNzYWdlID0gRkFMU0UsIA0KICByZXN1bHRzID0gVFJVRSwgDQogIGNvbW1lbnQgPSAiIikNCg0Kb3B0aW9ucyhyZ2wudXNlTlVMTD1UUlVFKQ0KDQprbml0cjo6a25pdF9ob29rcyRzZXQod2ViZ2wgPSBob29rX3dlYmdsKQ0KDQpgYGBgDQoNCiMgSW50cm9kdWN0aW9uDQoNCkluIE1hbW1hbHMsIGxpZmUgc3RhcnRzIHdoZW4gYSBtYWxlIHNwZXJtIGNlbGwgZmVydGlsaXplcyBhIGZlbWFsZSBvb2N5dGUgdG8gZm9ybSBhIHp5Z290ZS4gVGhpcyB1bmlxdWUgdG90aXBvdGVudCBjZWxsIGNhbiBnaXZlIHJpc2UgdG8gYSBjb21wbGV0ZSBtdWx0aWNlbGx1bGFyIG9yZ2FuaXNtLiBUaGlzIGZvcm1hdGlvbiByZWxpZXMgb24gYSBzZXJpZXMgb2YgY2VsbHVsYXIgZGlmZmVyZW50aWF0aW9uIGFuZCBzcGF0aWFsIG9yZ2FuaXphdGlvbiBldmVudHMuIE11Y2ggaW5zaWdodCBpbnRvIE1hbW1hbGlhbiBkZXZlbG9wbWVudCBoYXMgY29tZSBmcm9tIHRoZSBtb3VzZSwgd2hpY2ggYmVuZWZpdHMgZnJvbSBhIHJhcGlkIGRldmVsb3BtZW50ICgyMSBkYXlzKSBhbmQgcHJvdmlkZXMgdW5yaXZhbGxlZCBhY2Nlc3NpYmlsaXR5IHRvIGVtYnJ5b3MuIFJlZ2FyZGluZyBodW1hbiBkZXZlbG9wbWVudCBob3dldmVyLCBkaWZmZXJlbmNlcyBoYXZlIGVtZXJnZWQsIGFuZCBtYW55IHF1ZXN0aW9ucyByZW1haW4gdW5zb2x2ZWQgKFtNb2xlICpldCBhbCosIDIwMjBdKCNtb2xlXzIwMjApKS4gVGhhbmtzIHRvIHRoZSBhZHZlbnQgb2Ygc2luZ2xlLWNlbGwgbXVsdGktb21pY3MgdGVjaG5pcXVlcywgd2UgaGF2ZSBub3cgYWNjZXNzIHRvIHRoZSB2ZXJ5IGludGltYXRlIG1vbGVjdWxhciBwcm9jZXNzZXMgb2NjdXJpbmcgaW4gdGhlIGVhcmx5IGh1bWFuIGVtYnJ5by4gSG93ZXZlciwgZXhwbG9yYXRpb24gb2YgdGhlc2UgZGF0YSBoYXMgYmVlbiBvcHRpbWl6ZWQsIGFuZCBub3RhYmx5LCBhIGNhcmVmdWwgaW50ZWdyYXRlZCBhbmFseXNpcyBvZiB0aGUgZGlmZmVyZW50IG9taWNzIGxldmVscyBoYXMgbm90IGJlZW4gY29uZHVjdGVkIHNvIGZhci4NCg0KSW4gdGhpcyBzdHVkeSwgd2UgcHJvcG9zZSB0byBhcHBseSBpbnRlZ3JhdGVkIGJpb2luZm9ybWF0aWNzIGFuYWx5c2VzIHRvIHRyYW5zY3JpcHRvbWljIGFuZCBtZXRoeWxvbWljIGRhdGEgb2YgdGhlIHBlcmktaW1wbGFudGF0aW9uIGh1bWFuIGVtYnJ5bywgaW4gb3JkZXIgdG8gZ2FpbiBpbnNpZ2h0IGludG8gdGhlIGludGVyY29ubmVjdGlvbiBvZiB0aGVzZSBkaWZmZXJlbnQgbW9sZWN1bGFyIGxldmVscy4gRm9yIHRoaXMgcHVycG9zZSwgd2UgdXNlIHNpbmdsZS1jZWxsIFJOQS1TZXEgYW5kIFBCQVQgZGF0YSBmcm9tIChbWmhvdSAqZXQgYWwqLCAyMDE5XSgjemhvdV8yMDE5KSksIGV4dHJhY3RlZCBmcm9tICppbiB2aXRybyogY3VsdHVyZWQgaHVtYW4gZW1icnlvcyBmcm9tIGRheSA2IHRvIDEyIG9mIGRldmVsb3BtZW50LiBUaGVzZSBkYXRhIGFyZSBwYXJ0aWN1bGFybHkgc3VpdGVkIGZvciBvdXIgYW5hbHlzaXMsIGJlY2F1c2UgdHJhbnNjcmlwdG9taWMgYW5kIEROQSBtZXRoeWxhdGlvbiBkYXRhIHdlcmUgb2J0YWluZWQgZnJvbSB0aGUgc2FtZSBjZWxsLg0KDQpGaXJzdCwgd2Ugbm9ybWFsaXplIHRoZSBkYXRhIHRvIG1ha2UgdGhlbSBzdWl0YWJsZSBmb3Igc2Vjb25kYXJ5IGFuYWx5c2lzIGFsZ29yaXRobXMuIFRoZW4sIHdlIHN0YXJ0IGludmVzdGlnYXRpbmcgZWFjaCBkYXRhc2V0IHNlcGFyYXRlbHkuIFdlIGRldGVybWluZSBpbnRlci1pbmRpdmlkdWFsIHNpbWlsYXJpdGllcyBieSBkaW1lbnNpb24gcmVkdWN0aW9uIGFwcHJvYWNoZXMgc3VjaCBhcyBQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzIChQQ0EpLiBXZSBjcm9zcy12YWxpZGF0ZSB0aGUgcmVzdWx0cyB3aXRoIHVuc3VwZXJ2aXNlZCBoaWVyYXJjaGljYWwgY2x1c3RlcmluZy4NCg0KTmV4dCwgd2UgcGVyZm9ybSBpbnRlZ3JhdGVkIGFuYWx5c2lzIG9mIHRoZSB0d28gZGF0YXNldHMgd2l0aCBkaWZmZXJlbnQgYXBwcm9hY2hlczoNCg0KLSBNdWx0aS1PbWljcyBGYWN0b3IgQW5hbHlzaXMgKE1PRkEpDQotIEtlcm5lbCBjb21wdXRhdGlvbg0KDQpXZSBmb2xsb3cgdGhlIGFuYWx5c2lzIGJ5IGludmVzdGlnYXRpbmcgc2ltaWxhcml0aWVzIGJldHdlZW4gZ2VuZSBleHByZXNzaW9uIGFuZCBETkEgbWV0aHlsYXRpb24gcGF0dGVybnMgdGhyb3VnaCB0aGUgc2FtcGxlIHRoaXMgdGltZS4gRm9yIHRoaXMgcHVycG9zZSwgd2UgdXNlIFdlaWdodGVkIEdlbmUgQ29ycmVsYXRpb24gTmV0d29yayBBbmFseXNpcyAoV0dDTkEpIGluIG9yZGVyIHRvIGlkZW50aWZ5IGNvcnJlbGF0ZWQgZ2VuZSBtb2R1bGVzLg0KDQpGaW5hbGx5LCB3ZSBjb25zdWx0IHNlbGVjdGVkIGRhdGFiYXNlcyBmb3IgZnVuY3Rpb25hbCBlbnJpY2htZW50IGFuYWx5c2lzLCBhbmQgZHJhdyBjb25jbHVzaW9ucyBhbmQgcGVyc3BlY3RpdmVzDQoNCiMgRGF0YSBwcmVwcm9jZXNzaW5nDQoNCiMjIERhdGEgZXhwbG9yYXRpb24NCg0KVGhlc2UgdHdvIGRhdGFzZXRzIGNvbnNpc3Qgb2YgMTMwIGNlbGxzIGZyb20gdGhlIHRocmVlIGVhcmxpZXN0IGNlbGwgbGluZWFnZXM6DQoNCi0gdGhlIGVwaWJsYXN0IChFUEkpLCB3aGljaCBpcyBwbHVyaXBvdGVudCBhbmQgZ2l2ZSByaXNlIHRvIHRoZSBmZXR1cw0KLSB0aGUgcHJpbWl0aXZlIGVuZG9kZXJtIChQRSksIHdoaWNoIHByb2R1Y2VzIHRoZSB5b2xrIHNhYw0KLSB0aGUgdHJvcGhlY3RvZGVybSAoVEUpLCB3aGljaCB5aWVsZHMgdGhlIHBsYWNlbnRhLg0KDQpUaGUgdGFibGUgYmVsb3cgc3VtbWFyaXplcyB0aGUgZGlzdHJpYnV0aW9uIG9mIGNlbGxzIGludG8gbGluZWFnZXMgYW5kIGVtYnJ5b25pYyBkYXlzLg0KDQpgYGBge3IgZGF0YSBsb2FkaW5nfQ0KZG5hPWFzLm1hdHJpeChmYXN0UmVhZDIoImVtYnJ5b19kbmFfbWV0aHlsYXRpb25fZ29vZC50c3YiKSkNCmNvbG5hbWVzKGRuYSk9c3ViKCJeW01ldGhfVHJpX2h2X10rKCopIiwgIlxcMSIsIGNvbG5hbWVzKGRuYSkpDQpkbmFbaXMubmEoZG5hKV0gPC0gMA0KDQpybmE9YXMubWF0cml4KGZhc3RSZWFkMigiR1NFMTA5NTU1X1RyaW9TZXFfVFBNLnR4dCIpKQ0KY29sbmFtZXMocm5hKT1zdWIoIl5bVHJpX2h2X10rKCopIiwgIlxcMSIsIGNvbG5hbWVzKHJuYSkpDQpybmE9cm5hWyxjb2xuYW1lcyhkbmEpXQ0KDQptZXRhZGF0YT1mYXN0UmVhZDIoImVtYnJ5b19tZXRhZGF0YS50c3YiKVssYygiTGluZWFnZSIsIkRheSIsIlNleCIsIkRheV9FbWIiKV0NCnJvd25hbWVzKG1ldGFkYXRhKT1zdWIoIl5bTWV0aF9UcmlfaHZfXSsoKikiLCAiXFwxIiwgcm93bmFtZXMobWV0YWRhdGEpKQ0KY29sbmFtZXMobWV0YWRhdGEpPWMoImxpbmVhZ2UiLCJkYXkiLCJzZXgiLCJlbWJyeW8iKQ0KbWV0YWRhdGE9bWV0YWRhdGFbY29sbmFtZXMoZG5hKSxdDQoNCnJvd19saW5lYWdlPU5VTEwNCg0KZm9yIChsaW5lYWdlX2kgaW4gbGV2ZWxzKGZhY3RvcihtZXRhZGF0YSRsaW5lYWdlKSkpew0KICANCiAgbGluZWFnZV9kYXk9TlVMTA0KICANCiAgZm9yIChkYXlfaSBpbiBsZXZlbHMoZmFjdG9yKG1ldGFkYXRhJGRheSxsZXZlbHMgPSBjKCJENiIsIkQ4IiwiRDEwIiwiRDEyIikpKSl7DQogICAgbGluZWFnZV9kYXk9Y2JpbmQobGluZWFnZV9kYXksbnJvdyhtZXRhZGF0YVt3aGljaChtZXRhZGF0YSRsaW5lYWdlPT1saW5lYWdlX2kgJiBtZXRhZGF0YSRkYXk9PWRheV9pKSxdKSkNCiAgfQ0KICANCiAgcm93X2xpbmVhZ2U9cmJpbmQocm93X2xpbmVhZ2UsIGxpbmVhZ2VfZGF5KQ0KfQ0KDQpyb3duYW1lcyhyb3dfbGluZWFnZSk9bGV2ZWxzKGZhY3RvcihtZXRhZGF0YSRsaW5lYWdlKSkNCmNvbG5hbWVzKHJvd19saW5lYWdlKT1sZXZlbHMoZmFjdG9yKG1ldGFkYXRhJGRheSxsZXZlbHMgPSBjKCJENiIsIkQ4IiwiRDEwIiwiRDEyIikpKQ0Kcm93X2xpbmVhZ2U9Y2JpbmQocm93X2xpbmVhZ2UsVG90YWw9cm93U3Vtcyhyb3dfbGluZWFnZSkpDQprYWJsZShyb3dfbGluZWFnZSwgY2FwdGlvbiA9ICJUYWJsZSBvZiBlbWJyeW8gY2VsbCBmZWF0dXJlcyIpDQpgYGBgDQoNCmBgYGB7ciBhbm5vdGF0aW9uIG9iamVjdHN9DQpkYXk9bWV0YWRhdGFbY29sbmFtZXMoZG5hKSwiZGF5Il0NCm5hbWVzKGRheSk9Y29sbmFtZXMoZG5hKQ0KZGF5X2NvbG9ycz1jKCJEMTAiPSJibHVlIiwiRDEyIj0icmVkIiwiRDYiPSJvcmFuZ2UiLCJEOCI9InB1cnBsZSIpDQpkYXlfY29sb3JzIDwtIGRheV9jb2xvcnNbYXMuZmFjdG9yKGRheSldDQpkYXlfc2hhcGUgPSBjKCJEMTAiPSJzcXVhcmUiLCJEMTIiPSJjaXJjbGUiLCJENiI9ICJkaWFtb25kIiwiRDgiPSAieCIpIA0KZGF5X3NoYXBlIDwtIGRheV9zaGFwZVthcy5mYWN0b3IoZGF5KV0NCmRheV9jb2xvcl9wY2FfdW1hcD11bmlxdWUoZGF5X2NvbG9ycykNCm5hbWVzKGRheV9jb2xvcl9wY2FfdW1hcCk9dW5pcXVlKG5hbWVzKGRheV9jb2xvcnMpKQ0KZGF5X3NoYXBlX3BjYV91bWFwPXVuaXF1ZShkYXlfc2hhcGUpDQpuYW1lcyhkYXlfc2hhcGVfcGNhX3VtYXApPXVuaXF1ZShuYW1lcyhkYXlfc2hhcGUpKQ0KZGF5X3RyYWl0cz1tb2RlbC5tYXRyaXgofiAwICsgZGF5LCBkYXRhPWFzLmRhdGEuZnJhbWUoZGF5KSkNCg0KbGluZWFnZT1tZXRhZGF0YVtjb2xuYW1lcyhkbmEpLCJsaW5lYWdlIl0NCm5hbWVzKGxpbmVhZ2UpPWNvbG5hbWVzKGRuYSkNCmxpbmVhZ2VfY29sb3JzPWMoIkVwaSI9InJlZCIsIlBFIj0iZ3JlZW4iLCJURSI9ImJsdWUiKQ0KbGluZWFnZV9jb2xvcnMgPC0gbGluZWFnZV9jb2xvcnNbYXMuZmFjdG9yKGxpbmVhZ2UpXQ0KbGluZWFnZV9zaGFwZSA9IGMoIkVwaSI9InNxdWFyZSIsIlBFIj0iY2lyY2xlIiwiVEUiPSAiZGlhbW9uZCIpIA0KbGluZWFnZV9zaGFwZSA8LSBsaW5lYWdlX3NoYXBlW2FzLmZhY3RvcihsaW5lYWdlKV0NCmxpbmVhZ2VfY29sb3JfcGNhX3VtYXA9dW5pcXVlKGxpbmVhZ2VfY29sb3JzKQ0KbmFtZXMobGluZWFnZV9jb2xvcl9wY2FfdW1hcCk9dW5pcXVlKG5hbWVzKGxpbmVhZ2VfY29sb3JzKSkNCmxpbmVhZ2Vfc2hhcGVfcGNhX3VtYXA9dW5pcXVlKGxpbmVhZ2Vfc2hhcGUpDQpuYW1lcyhsaW5lYWdlX3NoYXBlX3BjYV91bWFwKT11bmlxdWUobmFtZXMobGluZWFnZV9zaGFwZSkpDQpsaW5lYWdlX3RyYWl0cz1tb2RlbC5tYXRyaXgofiAwICsgbGluZWFnZSwgZGF0YT1hcy5kYXRhLmZyYW1lKGxpbmVhZ2UpKQ0KDQp0cmFpdHM9Y2JpbmQoZGF5X3RyYWl0cyxsaW5lYWdlX3RyYWl0cykNCnRyYWl0c19tYWluPWNiaW5kKGFzLm51bWVyaWMoZmFjdG9yKG1ldGFkYXRhJGxpbmVhZ2UpKSxhcy5udW1lcmljKGZhY3RvcihtZXRhZGF0YSRkYXkpKSxhcy5udW1lcmljKGZhY3RvcihtZXRhZGF0YSRzZXgpKSxhcy5udW1lcmljKGZhY3RvcihtZXRhZGF0YSRlbWJyeW8pKSkNCmNvbG5hbWVzKHRyYWl0c19tYWluKT1jb2xuYW1lcyhtZXRhZGF0YSkNCnJvd25hbWVzKHRyYWl0c19tYWluKT1yb3duYW1lcyhtZXRhZGF0YSkNCmBgYGANCg0KIyMgTm9ybWFsaXNhdGlvbiBvZiBkYXRhIGRpc3RyaWJ1dGlvbg0KDQpJbnB1dCBkYXRhIGhhdmUgYmVlbiBwcmUtcHJvY2Vzc2VkIHRvIHNvbWUgZXh0ZW50OiBtZXRoeWxhdGlvbiBsZXZlbHMgcmFuZ2UgZnJvbSAwIHRvIDEgd2hpbGUgUk5BLVNlcSBjb3VudHMgYXJlIGdpdmVuIGluIEZQS00uIEhvd2V2ZXIsIGRpc3RyaWJ1dGlvbiBvZiBmZWF0dXJlIHZhbHVlcyBkbyBub3QgZm9sbG93IGEgR2F1c3NpYW4gbGF3LiBXZSBuZWVkIHRvIG5vcm1hbGlzZSB0aGUgZGF0YSBwcmlvciB0byBmdXJ0aGVyIHByb2Nlc3NpbmcsIGluIG9yZGVyIHRvIGluY3JlYXNlIHRoZSByb2J1c3RuZXNzIG9mIHNlY29uZGFyeSBhbmFseXNlcy4NCg0KQnkgbG9va2luZyBhdCB0b3RhbCBtZWFzdXJlcyBwZXIgaW5kaXZpZHVhbCBmb3IgZWFjaCBkYXRhc2V0LCB3ZSBvYnNlcnZlIHRoYXQgbGlicmFyeSBzaXplcyBmb3IgRE5BIG1ldGh5YWx0aW9uIGRpZmZlciB3aXRoaW4gc2FtcGxlLCBsaWtlbHkgZHVlIHRvIHRlY2huaWNhbCBpc3N1ZXMgcmVsYXRlZCB0byBzZXF1ZW5jaW5nIGRlcHRoLiBTbyB3ZSBkZWNpZGUgdG8gc3RhbmRhcmRpemUgdGhpcyBzaXplIGJ5IHNjYWxpbmcgdW5pdHMgdG8gMWU2LiBJbiBjb250cmFzdCwgZWFjaCBsaWJyYXJ5IGZyb20gUk5BLVNlcSBkYXRhc2V0IGhhcyB0aGUgc2FtZSBzaXplIGFzIGl0IGNvcnJlc3BvbmRzIHRvIEZQS00uDQoNCmBgYGB7ciBhcHBseSB1cG0gdG8gZG5hfQ0KIyBOb3JtYWxpemUgbGlicmFyeSBzaXplDQoNCmRuYT1kbmEqMTAwICN0aGlzIG9wZXJhdGlvbiBpcyBvbmx5IGludGVuZGVkIHRvIGZhY2lsaXRhdGUgdmlzdWFsaXphdGlvbiBvZiBkYXRhIGluaXRpYWxseSByYW5naW5nIGZyb20gMCB0byAxDQpkbmE9cm91bmQoZG5hLDIpICN0aGlzIG9wZXJhdGlvbiBpcyBvbmx5IGludGVuZGVkIHRvIGZhY2lsaXRhdGUgY29tcGFyaXNvbiBvZiBkaXN0cmlidXRpb24gYmV0d2VlbiB0aGUgdHdvIGRhdGFzZXRzLCBzYW1lIHJvdW5kaW5nIA0KZG5hX3VwbT1yb3VuZChhcHBseShkbmEsMixmdW5jdGlvbih4KXt4L3N1bSh4KSoxZTZ9KSwyKQ0KDQpgYGBgDQoNCldlIGNhbiBhbHNvIGZpbHRlciBvdXQgbnVsbCBhbmQgbG93LXZhcmlhbmNlIGZlYXR1cmVzLCBhcyB0aGVzZSBtaWdodCBub3QgY29udGFpbiByZWxldmFudCBpbmZvcm1hdGlvbiBmb3IgZGlmZmVyZW50aWFsIGFuYWx5c2lzIHdlIHBsYW4gdG8gY29uZHVjdC4NCg0KV2Ugc3RhcnQgY3JlYXRpbmcgdGFibGVzIHRoYXQgc3VtbWFyaXplIHF1YW50aXRhdGl2ZSBtZWFzdXJlcyBwZXIgaW5kaXZpZHVhbCBhbmQgcGVyIGZlYXR1cmUuDQoNCmBgYGB7ciBkbmEgbWV0aHlsYXRpb24gbG9jaSBhbmQgdHJhbnNjcmlwdHMgc3RhdGlzdGljc30NCg0KZG5hX3VwbV9nZW5lX3N0YXRfcHJlbm9ybSA8LSBkYXRhLmZyYW1lKA0KbWVhbj1hcHBseShkbmFfdXBtLDEsbWVhbiksDQpzZD1hcHBseShkbmFfdXBtLDEsc2QpLA0KdmFyPWFwcGx5KGRuYV91cG0sMSxzZCleMiwNCmN2PWFwcGx5KGRuYV91cG0sMSxmdW5jdGlvbih4KXtzZCh4KS9tZWFuKHgpfSksDQppcXI9YXBwbHkoZG5hX3VwbSwxLGZ1bmN0aW9uKHgpIHF1YW50aWxlKHgsIHByb2JzPXNlcSgwLDEsMC4wNSksIG5hLnJtPVQpKVsiNzUlIixdLWFwcGx5KGRuYV91cG0sMSxmdW5jdGlvbih4KSBxdWFudGlsZSh4LCBwcm9icz1zZXEoMCwxLDAuMDUpLCBuYS5ybT1UKSlbIjI1JSIsXSwNCm1pbj1hcHBseShkbmFfdXBtLDEsbWluKSwNCm1lZGlhbj1hcHBseShkbmFfdXBtLDEsbWVkaWFuKSwNCm1heD1hcHBseShkbmFfdXBtLDEsbWF4KSwNCm51bGwgPSBhcHBseShkbmFfdXBtID09IDAsIDEsIHN1bSwgbmEucm0gPSBUUlVFKQ0KKQ0KDQpybmFfZ2VuZV9zdGF0X3ByZW5vcm0gPC0gZGF0YS5mcmFtZSgNCm1lYW49YXBwbHkocm5hLDEsbWVhbiksDQpzZD1hcHBseShybmEsMSxzZCksDQp2YXI9YXBwbHkocm5hLDEsc2QpXjIsDQpjdj1hcHBseShybmEsMSxmdW5jdGlvbih4KXtzZCh4KS9tZWFuKHgpfSksDQppcXI9YXBwbHkocm5hLDEsZnVuY3Rpb24oeCkgcXVhbnRpbGUoeCwgcHJvYnM9c2VxKDAsMSwwLjA1KSwgbmEucm09VCkpWyI3NSUiLF0tYXBwbHkocm5hLDEsZnVuY3Rpb24oeCkgcXVhbnRpbGUoeCwgcHJvYnM9c2VxKDAsMSwwLjA1KSwgbmEucm09VCkpWyIyNSUiLF0sDQptaW49YXBwbHkocm5hLDEsbWluKSwNCm1lZGlhbj1hcHBseShybmEsMSxtZWRpYW4pLA0KbWF4PWFwcGx5KHJuYSwxLG1heCksDQpudWxsID0gYXBwbHkocm5hID09IDAsIDEsIHN1bSwgbmEucm0gPSBUUlVFKQ0KKQ0KDQpgYGBgDQoNCmBgYGB7ciBmaWx0ZXJpbmcgb3V0IG51bGwgZmVhdHVyZXN9DQoNCiMjIERhdGEgZmlsdGVyaW5nOiBnZW5lcyBoYXZpbmcgYXQgbGVhc3QgOTAlIG51bGwgdmFsdWVzDQptZXRoX3Byb21fbnVsbCA8LSBkbmFfdXBtX2dlbmVfc3RhdF9wcmVub3JtJG51bGwgPj0gbmNvbChkbmFfdXBtKQ0KdHJhbnNjcmlwdF9udWxsIDwtIHJuYV9nZW5lX3N0YXRfcHJlbm9ybSRudWxsID49IG5jb2wocm5hKQ0KDQptZXRoX3Byb21fbG93X3ZhciA8LSBkbmFfdXBtX2dlbmVfc3RhdF9wcmVub3JtJHZhciA8PSBtZWRpYW4oZG5hX3VwbV9nZW5lX3N0YXRfcHJlbm9ybSR2YXIpDQp0cmFuc2NyaXB0X2xvd192YXIgPC0gcm5hX2dlbmVfc3RhdF9wcmVub3JtJHZhciA8PSBtZWRpYW4ocm5hX2dlbmVfc3RhdF9wcmVub3JtJHZhcikNCg0KbWV0aF9wcm9tX3N1YnNldCA9ICEobWV0aF9wcm9tX251bGwgfCBtZXRoX3Byb21fbG93X3ZhcikNCnRyYW5zY3JpcHRfc3Vic2V0ID0gISh0cmFuc2NyaXB0X251bGwgfCB0cmFuc2NyaXB0X2xvd192YXIpDQoNCmRuYV91cG1fc3Vic2V0ID0gZG5hX3VwbVttZXRoX3Byb21fc3Vic2V0LF0NCnJuYV9zdWJzZXQgPSBybmFbdHJhbnNjcmlwdF9zdWJzZXQsXQ0KDQpgYGBgDQoNCmBgYGB7ciBFeHBsb3Jpbmcgc2NQQkFUIGlucHV0IGRhdGEsIGZpZ3VyZXMtc2lkZSwgZmlnLnNob3c9ImhvbGQiLCBvdXQud2lkdGg9IjMzJSIsZmlnLmhvbGQ9VFJVRSxmaWcudG9wY2FwdGlvbiA9IFRSVUUgLCBmaWcuY2FwPSI8Y2VudGVyPioqTm9ybWFsaXNhdGlvbiBvZiBnZW5lIHByb21vdGVyIG1ldGh5bGF0aW9uIGxldmVsIGRpc3RyaWJ1dGlvbioqPC9jZW50ZXI+In0NCg0KaGlzdChkbmEsIGJyZWFrcz0xMDAsIG1haW4gPSAiSW5wdXQiKQ0KaGlzdChsb2cyKGRuYV91cG0pLCBicmVha3M9MTAwLCBtYWluID0gIlNpemUgc3RhbmRhcmRpc2F0aW9uICYgbG9nLXRyYW5zZm9ybWF0aW9uIikNCmhpc3QobG9nMihkbmFfdXBtX3N1YnNldCksIGJyZWFrcz0xMDAsIG1haW4gPSAiU2l6ZSBzdGFuZGFyZGlzYXRpb24sIGZpbHRyYXRpb24gJiBsb2ctdHJhbnNmb3JtYXRpb24iKQ0KDQpgYGBgDQoNCmBgYGB7ciBFeHBsb3Jpbmcgc2NSTkEtU2VxIGlucHV0IGRhdGEsIGZpZ3VyZXMtc2lkZSwgZmlnLnNob3c9ImhvbGQiLCBvdXQud2lkdGg9IjMzJSIsZmlnLmhvbGQ9VFJVRSxmaWcudG9wY2FwdGlvbiA9IFRSVUUgLCBmaWcuY2FwPSI8Y2VudGVyPioqTm9ybWFsaXNhdGlvbiBvZiBnZW5lIHByb21vdGVyIG1ldGh5bGF0aW9uIGxldmVscyoqPC9jZW50ZXI+In0NCg0KaGlzdChybmEsIGJyZWFrcz0xMDAsIG1haW4gPSAiSW5wdXQiKQ0KaGlzdChsb2cyKHJuYSksIGJyZWFrcz0xMDAsIG1haW4gPSAiTG9nLXRyYW5zZm9ybWF0aW9uIikNCmhpc3QobG9nMihybmFfc3Vic2V0KSwgYnJlYWtzPTEwMCwgbWFpbiA9ICJTaXplIHN0YW5kYXJkaXNhdGlvbiwgZmlsdHJhdGlvbiAmIGxvZy10cmFuc2Zvcm1hdGlvbiIpDQoNCmBgYGANCldoZW4gd2UgcGxvdCBpbnB1dCBkYXRhIGZvciB0aGUgdHdvIGRhdGFzZXRzLCB3ZSBvYnNlcnZlIGEgMCBpbmZsYXRpb24sIHdoaWNoIGNhbiBoYXZlIHR3byBvcmlnaW5zOg0KDQotIGJpb2xvZ2ljYWw6IDAgZXhwcmVzc2lvbiBvciBtZXRoeWxhdGlvbiwgdW5saWtlbHkNCi0gdGVjaG5pY2FsOiBubyBkZXRlY3Rpb24gb2YgZXhwcmVzc2lvbiBvciBtZXRoeWxhdGlvbiwgbGlrZWx5DQoNCk1vcmVvdmVyLCB3ZSBvYnNlcnZlIHRoYXQgZGlzdHJpYnV0aW9uIGlzIGFsc28gc2tld2VkIGJ5IGhpZ2ggdmFsdWVzLg0KDQpTbyBmb3IgYmV0dGVyIHZpc3VhbGl6YXRpb24gb2YgdGhlIGxhdGVudCBkaXN0cmlidXRpb24sIHdlIGFwcGx5IGxvZyB0cmFuc2Zvcm1hdGlvbiBvZiB0aGUgZGF0YSBpbiBvcmRlciB0byByZW1vdmUgMCBhbmQgcmVkdWNlIGR5bmFtaWMgcmFuZ2Ugb2YgdmFyaWFibGVzLg0KDQpCeSBkb2luZyB0aGlzLCB3ZSByZXZlYWwgdGhhdCBiaW9sb2dpY2FsbHkgcmVsYXRlZCBkaXN0cmlidXRpb24gb2YgZ2VuZSBleHByZXNzaW9uIGFuZCBETkEgbWV0aHlsYXRpb24gbGV2ZWxzIGF0IGdlbmUgcHJvbW90ZXJzLCBhZnRlciBmaWx0ZXJpbmcgb3V0IGxvdy12YXJpYW5jZSBmZWF0dXJlcywgYm90aCBhcHByb3hpbWF0ZSB0aGUgbm9ybWFsL0dhdXNzaWFuIGxhdy4NCg0KSXQgaXMgaW50ZXJlc3RpbmcgdG8gbWVudGlvbiB0aGF0IGxvdy12YXJpYW5jZSBsZXZlbHMgY29ycmVzcG9uZCB0byBsb3cgbWV0aHlsYXRlZCByZWdpb25zLCBhbmQgaWYgbm90IGZpbHRlcmVkIG91dCwgeWllbGQgYSBiaW1vZGFsIGRpc3RyaWJ1dGlvbi4NCg0KYGBgYHtyIGNsdXN0ZXJpbmcgb24gRE5BIG1ldGh5bGF0ZWQgZ2VuZSBwcm9tb3RlcnMsIGZpZ3VyZXMtc2lkZSwgZmlnLnNob3c9ImhvbGQiLCBvdXQud2lkdGg9IjUwJSIsZmlnLmhvbGQ9VFJVRSxmaWcudG9wY2FwdGlvbiA9IFRSVUUgLCBmaWcuY2FwPSI8Y2VudGVyPioqSGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgb2YgZW1icnlvIGNlbGxzIG9uIGdlbmUgcHJvbW90ZXIgbWV0aHlsYXRpb24qKjwvY2VudGVyPiJ9DQoNCm1ldGhfcHJvbV9zYW1wbGVfZGlzdCA8LSBmYWN0b2V4dHJhOjpnZXRfZGlzdCh4ID10KGxvZzIoZG5hX3VwbV9zdWJzZXQrMSkpLCBtZXRob2QgPSAicGVhcnNvbiIpDQoNCm1ldGhfcHJvbV9zYW1wbGVfZGlzdCAlPiUNCiAgaGNsdXN0KG1ldGhvZD0id2FyZC5EMiIpICU+JQ0KICBjb2xvcl9icmFuY2hlcyhrPTIsY29sID0gYygiIzAwRkZGRiIsIiNGRjAwMDAiKSwgZ3JvdXBMYWJlbHMgPSBUKSAtPiBtZXRoX3Byb21fc2FtcGxlX2RlbmQNCg0KbGFiZWxzX2NvbG9ycyhtZXRoX3Byb21fc2FtcGxlX2RlbmQpIDwtIHJhaW5ib3coNClbZmFjdG9yKGRheVtvcmRlci5kZW5kcm9ncmFtKG1ldGhfcHJvbV9zYW1wbGVfZGVuZCldKV0NCg0KcGxvdChtZXRoX3Byb21fc2FtcGxlX2RlbmQpDQoNCg0KbWV0aF9wcm9tX3NhbXBsZV9kaXN0IDwtIGZhY3RvZXh0cmE6OmdldF9kaXN0KHggPXQobG9nMihkbmFfdXBtX3N1YnNldCsxKSksIG1ldGhvZCA9ICJwZWFyc29uIikNCg0KbWV0aF9wcm9tX3NhbXBsZV9kaXN0ICU+JQ0KICBoY2x1c3QobWV0aG9kPSJ3YXJkLkQyIikgJT4lDQogIGNvbG9yX2JyYW5jaGVzKGs9Mixjb2wgPSBjKCIjMDBGRkZGIiwiI0ZGMDAwMCIpLCBncm91cExhYmVscyA9IFQpIC0+IG1ldGhfcHJvbV9zYW1wbGVfZGVuZA0KDQpsYWJlbHNfY29sb3JzKG1ldGhfcHJvbV9zYW1wbGVfZGVuZCkgPC0gcmFpbmJvdygzKVtmYWN0b3IobGluZWFnZVtvcmRlci5kZW5kcm9ncmFtKG1ldGhfcHJvbV9zYW1wbGVfZGVuZCldKV0NCg0KcGxvdChtZXRoX3Byb21fc2FtcGxlX2RlbmQpDQpgYGBgDQoNCmBgYGB7ciBjbHVzdGVyaW5nIG9uIFJOQSBnZW5lIGV4cHJlc3Npb24sIGZpZ3VyZXMtc2lkZSwgZmlnLnNob3c9ImhvbGQiLCBvdXQud2lkdGg9IjUwJSIsZmlnLmhvbGQ9VFJVRSxmaWcudG9wY2FwdGlvbiA9IFRSVUUgLCBmaWcuY2FwPSI8Y2VudGVyPioqSGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgb2YgZW1icnlvIGNlbGxzIG9uIGdlbmUgZXhwcmVzc2lvbioqPC9jZW50ZXI+In0NCnRyYW5zY3JpcHRfc2FtcGxlX2Rpc3QgPC0gZmFjdG9leHRyYTo6Z2V0X2Rpc3QoeCA9dChsb2cyKHJuYV9zdWJzZXQrMSkpLCBtZXRob2QgPSAicGVhcnNvbiIpDQoNCnRyYW5zY3JpcHRfc2FtcGxlX2Rpc3QgJT4lDQogIGhjbHVzdChtZXRob2Q9IndhcmQuRDIiKSAlPiUNCiAgY29sb3JfYnJhbmNoZXMoaz0xMCxjb2wgPSBjKCJibHVlIiwiYmx1ZSIsImdyZWVuIiwiYmx1ZSIsInJlZCIsInJlZCIsInJlZCIsImdyZWVuIiwiZ3JlZW4iLCJncmVlbiIpLCBncm91cExhYmVscyA9IFQpIC0+IHRyYW5zY3JpcHRfc2FtcGxlX2RlbmQNCg0KbGFiZWxzX2NvbG9ycyh0cmFuc2NyaXB0X3NhbXBsZV9kZW5kKSA8LSByYWluYm93KDQpW2ZhY3RvcihkYXlbb3JkZXIuZGVuZHJvZ3JhbSh0cmFuc2NyaXB0X3NhbXBsZV9kZW5kKV0pXQ0KDQpwbG90KHRyYW5zY3JpcHRfc2FtcGxlX2RlbmQpDQoNCg0KdHJhbnNjcmlwdF9zYW1wbGVfZGlzdCA8LSBmYWN0b2V4dHJhOjpnZXRfZGlzdCh4ID10KGxvZzIocm5hX3N1YnNldCsxKSksIG1ldGhvZCA9ICJwZWFyc29uIikNCg0KdHJhbnNjcmlwdF9zYW1wbGVfZGlzdCAlPiUNCiAgaGNsdXN0KG1ldGhvZD0id2FyZC5EMiIpICU+JQ0KICBjb2xvcl9icmFuY2hlcyhrPTEwLGNvbCA9IGMoImJsdWUiLCJibHVlIiwiZ3JlZW4iLCJibHVlIiwicmVkIiwicmVkIiwicmVkIiwiZ3JlZW4iLCJncmVlbiIsImdyZWVuIiksIGdyb3VwTGFiZWxzID0gVCkgLT4gdHJhbnNjcmlwdF9zYW1wbGVfZGVuZA0KDQpsYWJlbHNfY29sb3JzKHRyYW5zY3JpcHRfc2FtcGxlX2RlbmQpIDwtIHJhaW5ib3coMylbZmFjdG9yKGxpbmVhZ2Vbb3JkZXIuZGVuZHJvZ3JhbSh0cmFuc2NyaXB0X3NhbXBsZV9kZW5kKV0pXQ0KDQpwbG90KHRyYW5zY3JpcHRfc2FtcGxlX2RlbmQpDQoNCmBgYGANCg0KV2UgY2hvb3NlIHRvIGNhbGN1bGF0ZSBQZWFyc29uIGNvcnJlbGF0aW9uIGRpc3RhbmNlIGJldHdlZW4gRE5BIG1ldGh5bGF0ZWQgZ2VuZSBwcm9tb3RlcnMsIGJlY2F1c2UgdGhpcyBtZXRob2QgcHJvZHVjZXMgdGhlIGJlc3QgUmFuZCBBZGp1c3RlZCBJbmRleCAoW0phc2tvd2lhayAqZXQgYWwqLCAyMDE0XSgjamFza293aWFrXzIwMTQpKS4gVGhlbiB3ZSBhcHBseSBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyB3aXRoIFdhcmQgbWV0aG9kICgid2FyZC5EMiIpLCBhcyB0aGlzIG1vc3Qgcm9idXN0bHkgaWRlbnRpZmllcyBncm91cHMgb2YgaW5kaXZpZHVhbHMgd2l0aCBzaW1pbGFyIHF1YW50aXRhdGl2ZSB0cmFpdHMuIEluZGVlZCwgdGhpcyBncm91cHMgaW5kaXZpZHVhbHMgYWNjb3JkaW5nIHRvIHZhcmlhbmNlIGluc3RlYWQgb2YgbWVhbiwgY29udHJhcnkgdG8gb3RoZXIgbWV0aG9kcyBzdWNoIGFzICJhdmVyYWdlIiAoW0xhd2xvciAqZXQgYWwqLCAyMDE2XSgjbGF3bG9yXzIwMTYpKS4NCg0KRm9yIG1ldGh5bGF0aW9uIGxldmVscyBvZiBnZW5lIHByb21vdGVycywgd2Ugb2JzZXJ2ZSB0aGF0IHRoZSB0d28gbWFpbiBjbHVzdGVycyBzZWdyZWdhdGUgZGF5IDYgZnJvbSBsYXRlciBkZXZlbG9wbWVudGFsIHN0YWdlcy4gSG93ZXZlciwgd2l0aGluIHRoZSBzZWNvbmQgbWFpbiBjbHVzdGVyLCB3ZSBzZWUgdGhhdCBzdWJjbHVzdGVycyB3ZWxsIHNlcGFyYXRlIGNlbGwgbGluZWFnZXMuIEZvciBzdWJzZXF1ZW50IGFuYWx5c2VzLCB3ZSB3aWxsIGNvbnNpZGVyIGFuYWx5emluZyBkYXk2IGVpdGhlciB0b2dldGhlciBvciBzZXBhcmF0ZWx5IGZyb20gbGF0ZXIgc3RhZ2VzLCBkZXBlbmRpbmcgb24gdGhlIHF1ZXN0aW9uIHRvIGJlIGFkZHJlc3NlZC4NCg0KRm9yIGdlbmUgZXhwcmVzc2lvbiwgd2Ugb2JzZXJ2ZSB0aGF0IGNsdXN0ZXJzIG5pY2VseSBzZWdyZWdhdGUgY2VsbCBsaW5lYWdlcy4NCg0KIyBEaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gYmFzZWQgYW5hbHlzaXMgb2YgaW50ZXItaW5kaXZpZHVhbCB2aWNpbml0eQ0KDQpJbiBvcmRlciB0byBpbnZlc3RpZ2F0ZSBpbmRpdmlkdWFsIHZpY2luaXR5IHdpdGhpbiBzYW1wbGUsIHdlIHBlcmZvcm0gYSBQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzIChQQ0EpLCB3aGljaCByZWR1Y2VzIGRpbWVuc2lvbnMgd2hpbGUga2VlcGluZyBtYXhpbXVtIG9mIHNhbXBsZSB2YXJpYW5jZSBzdW1tYXJpc2VkIGluICJlaWdlbnZlY3RvcnMiIChbTGV2ZXIgKmV0IGFsKiwgMjAxN10oI2xldmVyXzIwMTcpKS4NCg0KV2UgYWxzbyBwZXJmb3JtIGFub3RoZXIgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIGFuYWx5c2lzLCB0aGUgVW5pZm9ybSBNYW5pZm9sZCBBcHByb3hpbWF0aW9uIGFuZCBQcm9qZWN0aW9uIChVTUFQKSwgd2hpY2ggaXMgY29tcGxlbWVudGFyeSB0byBQQ0EsIGFzIGl0IGlzIG5vbmxpbmVhciwgYW5kIG91dHBlcmZvcm1zIHQtU05FIChbS29iYWsgKmV0IGFsKiwgMjAxOV0oI2tvYmFrXzIwMTkpKS4gQSBrZXkgcGFyYW1ldGVyIGZvciB0aGUgVU1BUCBpcyB0aGUgbnVtYmVyIG9mIG5laWdoYm91cnMgZm9yIGVhY2ggaW5kaXZpZHVhbC4gSW4gb3JkZXIgdG8gZGV0ZXJtaW5lIHRoZSBtb3N0IGFwcHJvcHJpYXRlIG51bWJlciBvZiBuZWlnaGJvdXJzIGFjY29yZGluZyB0byB0aGUgbGF0ZW50IHN0cnVjdHVyZSBvZiB0aGUgZGF0YSwgd2UgdXNlIHRoZSByZXN1bHQgb2YgdGhlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIG9mIGNlbGxzLCBjdXQgYXQgdGhyZWUgY2x1c3RlcnMgcmVjYXBpdHVsYXRpbmcgZGV2ZWxvcG1lbnRhbCBkYXlzIG9yIGNlbGwgdHlwZXMgcmVsYXRpdmUgdG8gdGhlIGRhdGFzZXQsIGFuZCBhcHBseSB0aGUgbWVhbiBudW1iZXIgb2YgY2VsbHMgcGVyIGNsdXN0ZXIuDQoNCmBgYGB7ciBwY2EgYW5kIHVtYXAgb2YgZW1icnlvIGNlbGxzIGNvbXB1dGVkIG9uIGdlbmUgcHJvbW90ZXIgbWV0aHlsYXRpb24sZmlnLnRvcGNhcHRpb24gPSBUUlVFICwgZmlnLmNhcD0iPGNlbnRlcj4qKlBDQSBhbmQgVU1BUCBvZiBlbWJyeW8gY2VsbHMgY29tcHV0ZWQgb24gZ2VuZSBwcm9tb3RlciBtZXRoeWxhdGlvbioqPC9jZW50ZXI+In0NCmRuYV9wY2E8LUFDUChsb2cyKGRuYV91cG1fc3Vic2V0KzEpKQ0KDQpkbmFfdW1hcDwtbWFrZS51bWFwMihsb2cyKGRuYV91cG1fc3Vic2V0KzEpLG5fY29tcG9uZW50cyA9IDMsbl9uZWlnaGJvcnMgPSBuY29sKGRuYV91cG1fc3Vic2V0KSxtaW5fZGlzdCA9IDAuMikjZ29vZA0KDQpybmFfcGNhPC1BQ1AobG9nMihybmFfc3Vic2V0KzEpKQ0KDQpybmFfdW1hcDwtbWFrZS51bWFwMihsb2cyKHJuYV9zdWJzZXQrMSksbl9jb21wb25lbnRzID0gMyxuX25laWdoYm9ycyA9IG5jb2wocm5hX3N1YnNldCksbWluX2Rpc3QgPSAwLjIsIG1ldHJpYz0gImNvcnJlbGF0aW9uIikjZ29vZA0KDQojIHNhdmUuaW1hZ2UoIjIwMjFfMDZfMDdfZW1icnlvX3VtYXAuUkRhdGEiKQ0KbG9hZChmaWxlID0gIjIwMjFfMDZfMDdfZW1icnlvX3VtYXAuUkRhdGEiKQ0KDQpkbmFfcGNhX2ZpZyA8LSBwbG90X2x5KHg9ZG5hX3BjYSR4WywiUEMxIl0sIHk9ZG5hX3BjYSR4WywiUEMyIl0sIHo9ZG5hX3BjYSR4WywiUEMzIl0sY29sb3IgPSB+bmFtZXMoZGF5X2NvbG9ycyksIGNvbG9ycyA9IGRheV9jb2xvcl9wY2FfdW1hcCxhbHBoYT0wLjYsc2NlbmU9J3NjZW5lMScpDQpkbmFfcGNhX2ZpZyA8LSBkbmFfcGNhX2ZpZyAlPiUgYWRkX21hcmtlcnMoKQ0KDQpkbmFfdW1hcF9maWcgPC0gcGxvdF9seSh4PWRuYV91bWFwWywxXSwgeT1kbmFfdW1hcFssMl0sIHo9ZG5hX3VtYXBbLDNdLGNvbG9yID0gfm5hbWVzKGRheV9jb2xvcnMpLCBjb2xvcnMgPSBkYXlfY29sb3JfcGNhX3VtYXAsYWxwaGE9MC42LHN5bWJvbCA9IH5uYW1lcyhsaW5lYWdlX3NoYXBlKSwgc3ltYm9scz1saW5lYWdlX3NoYXBlX3BjYV91bWFwLHNjZW5lPSdzY2VuZTInKQ0KZG5hX3VtYXBfZmlnIDwtIGRuYV91bWFwX2ZpZyAlPiUgYWRkX21hcmtlcnMoKQ0KDQpkbmFfcGNhX3VtYXBfZmlnIDwtIHN1YnBsb3QoZG5hX3BjYV9maWcsIGRuYV91bWFwX2ZpZykgDQpkbmFfcGNhX3VtYXBfZmlnIDwtIGRuYV9wY2FfdW1hcF9maWcgJT4lIGxheW91dCh0aXRsZSA9ICIzRCBTdWJwbG90cyIsDQogICAgICAgICBzY2VuZTEgPSBsaXN0KGRvbWFpbj1saXN0KHg9YygwLDAuNSkseT1jKDAsMSkpLA0KICAgICAgICAgICAgICAgICAgICAgIGFzcGVjdG1vZGU9J2N1YmUnKSwNCiAgICAgICAgIHNjZW5lMiA9IGxpc3QoZG9tYWluPWxpc3QoeD1jKDAuNSwxKSx5PWMoMCwxKSksDQogICAgICAgICAgICAgICAgICAgICAgIGFzcGVjdG1vZGU9J2N1YmUnKSkNCg0KZG5hX3BjYV91bWFwX2ZpZw0KDQpgYGBgDQoNCmBgYGB7ciBwY2EgYW5kIHVtYXAgb2YgZW1icnlvIGNlbGxzIGNvbXB1dGVkIG9uIGdlbmUgZXhwcmVzc2lvbixmaWcudG9wY2FwdGlvbiA9IFRSVUUgLCBmaWcuY2FwPSI8Y2VudGVyPioqUENBIGFuZCBVTUFQIG9mIGVtYnJ5byBjZWxscyBjb21wdXRlZCBvbiBnZW5lIGV4cHJlc3Npb24qKjwvY2VudGVyPiJ9DQoNCnJuYV9wY2FfZmlnIDwtIHBsb3RfbHkoeD1ybmFfcGNhJHhbLCJQQzEiXSwgeT1ybmFfcGNhJHhbLCJQQzIiXSwgej1ybmFfcGNhJHhbLCJQQzMiXSxjb2xvciA9IH5uYW1lcyhsaW5lYWdlX2NvbG9ycyksIGNvbG9ycyA9IGxpbmVhZ2VfY29sb3JfcGNhX3VtYXAsYWxwaGE9MC42LCBzY2VuZT0nc2NlbmUxJykNCnJuYV9wY2FfZmlnIDwtIHJuYV9wY2FfZmlnICU+JSBhZGRfbWFya2VycygpDQoNCnJuYV91bWFwX2ZpZyA8LSBwbG90X2x5KHg9cm5hX3VtYXBbLDFdLCB5PXJuYV91bWFwWywyXSwgej1ybmFfdW1hcFssM10sY29sb3IgPSB+bmFtZXMobGluZWFnZV9jb2xvcnMpLCBjb2xvcnMgPSBsaW5lYWdlX2NvbG9yX3BjYV91bWFwLGFscGhhPTAuNixzeW1ib2wgPSB+bmFtZXMoZGF5X3NoYXBlKSwgc3ltYm9scz1kYXlfc2hhcGVfcGNhX3VtYXAsIHNjZW5lPSdzY2VuZTInKQ0Kcm5hX3VtYXBfZmlnIDwtIHJuYV91bWFwX2ZpZyAlPiUgYWRkX21hcmtlcnMoKQ0KDQpybmFfcGNhX3VtYXBfZmlnIDwtIHN1YnBsb3Qocm5hX3BjYV9maWcsIHJuYV91bWFwX2ZpZykgDQpybmFfcGNhX3VtYXBfZmlnIDwtIHJuYV9wY2FfdW1hcF9maWcgJT4lIGxheW91dCh0aXRsZSA9ICIzRCBTdWJwbG90cyIsDQogICAgICAgICBzY2VuZTEgPSBsaXN0KGRvbWFpbj1saXN0KHg9YygwLDAuNSkseT1jKDAsMSkpLA0KICAgICAgICAgICAgICAgICAgICAgIGFzcGVjdG1vZGU9J2N1YmUnKSwNCiAgICAgICAgIHNjZW5lMiA9IGxpc3QoZG9tYWluPWxpc3QoeD1jKDAuNSwxKSx5PWMoMCwxKSksDQogICAgICAgICAgICAgICAgICAgICAgIGFzcGVjdG1vZGU9J2N1YmUnKSkNCg0Kcm5hX3BjYV91bWFwX2ZpZw0KIyBzYXZlLmltYWdlKHBhc3RlMCh0aW1lTm93KCksImVtYnJ5b19kbmFfcm5hX3VtYXAuUkRhdGEiKSkNCmBgYGANCg0KV2Ugb2JzZXJ2ZSB0aGF0IGFwcGx5aW5nIHRoZSAiY29ycmVsYXRpb24iIG1ldHJpYyBiYXNlZCBvbiBQZWFyc29uIGRpc3RhbmNlIHRvIHVtYXAgY2FsY3VsYXRpb24gcHJvdmlkZXMgbXVjaCBiZXR0ZXIgcmVzdWx0cyB0aGFuIGV1Y2xpZGVhbiBkaXN0YW5jZS4NCg0KUENBIGFuZCBVTUFQIHlpZWxkIGdyb3VwcyBvZiBpbmRpdmlkdWFscyBpbiBsaW5lIHdpdGggaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcuIEZvciBib3RoIGFuYWx5c2VzLCB3ZSBvYnNlcnZlIHRoYXQgZ2VuZSBwcm9tb3RlciBtZXRoeWxhdGlvbiBtYWlubHkgcmVjYXBpdHVsYXRlcyBkZXZlbG9wbWVudGFsIHByb2dyZXNzaW9uIChlbWJyeW9uaWMgZGF5cykgd2hpbGUgZ2VuZSBleHByZXNzaW9uIHJlY2FwaXR1bGF0ZXMgY2VsbCBsaW5lYWdlcyhFUEksIFBFLCBURSkuIFRoaXMgb2JzZXJ2YXRpb24gaXMgaW4gbGluZSB3aXRoIHByZXZpb3VzbHkgcHVibGlzaGVkIHJlc3VsdHMgZnJvbSAoW1pob3UgKmV0IGFsKiwgMjAxOV0oI3pob3VfMjAxOSkpLCBhcyBzaG93biBpbiB0aGUgZmlndXJlIGJlbG93Og0KDQoiUHJpbmNpcGFsIGNvbXBvbmVudCBhbmFseXNpcyANCm9mIEROQSBtZXRoeWxvbWUgZGF0YSBzaG93ZWQgdGhhdCB0aGVzZSAxMzAgY2VsbHMgZm9ybWVkIDQgbWFqb3IgDQpjbHVzdGVycyAoRXh0ZW5kZWQgRGF0YSBGaWcuIDhnLCBoKSwgd2l0aCBhIGNvbWJpbmF0aW9uIG9mIHRoZSBFUEksIFBFIA0KYW5kIFRFIGF0IHRoZSBibGFzdG9jeXN0IHN0YWdlIChkYXkgNikgYXMgYSBzaW5nbGUgY2x1c3RlciwgYW5kIHRoZSBFUEksIA0KUEUgYW5kIFRFIGJleW9uZCB0aGUgYmxhc3RvY3lzdCBzdGFnZSBhcyBhbm90aGVyIDMgc2VwYXJhdGUgY2x1c3RlcnMsIA0Kc3VnZ2VzdGluZyB0aGF0IGFsbCBvZiB0aGUgMyBsaW5lYWdlcyBzaG93ZWQgY29uc2lkZXJhYmxlIGNoYW5nZXMgaW4gDQpETkEgbWV0aHlsYXRpb24gc29vbiBhZnRlciBpbXBsYW50YXRpb24uIg0KDQpJbiB0ZXJtcyBvZiBiaW9sb2d5LCB0aGlzIGNvdWxkIG1lYW4gdGhhdCBhdCBkYXkgNiwgZ2xvYmFsIEROQSBtZXRoeWxhdGlvbiBzdGF0ZSByZXN1bHRzIGZyb20gZXBpZ2VuZXRpYyB3YXZlcyBhdCBlYXJseSBlbWJyeW9uaWMgc3RhZ2VzLCBwcmlvciB0byBsaW5lYWdlIHNwZWNpZmljYXRpb24uIExhdGVyIHN0YWdlcyBzaG93IHJld3JpdGluZyBvZiBlcGlnZW5ldGljIG1hcmtzIHNwZWNpZmljIHRvIGNlbGwgbGluZWFnZXMsIHdoaWNoIGhhdmUgZGlzdGluY3QgdHJhbnNjcmlwdG9taWMgc2lnbmF0dXJlcy4NCg0KVGhpcyBvYnNlcnZhdGlvbiBpcyBpbnRlcmVzdGluZyBhcyBhIGNvbWJpbmF0aW9uIG9mIEROQSBtZXRoeWxhdGlvbiBhbmQgZ2VuZSBleHByZXNzaW9uIGxldmVscyBtaWdodCBhbGxvdyB0byBhc3NpZ24gZGV2ZWxvcG1lbnRhbCBzdGFnZSBhbmQgbGluZWFnZSB0byBzaW5nbGUgY2VsbHMgb2YgdGhlIGh1bWFuIGVtYnJ5by4NCg0KIyBXR0NOQQ0KDQpBZnRlciBjb25zaWRlcmluZyBpbnRlci1pbmRpdmlkdWFsIHZpY2luaXR5LCB3ZSBub3cgaW52ZXN0aWdhdGUgY29ycmVsYXRpb24gd2l0aGluIHNhbXBsZSBhdCBnZW5lIHByb21vdGVyIG1ldGh5bGF0aW9uIGFuZCBnZW5lIGV4cHJlc3Npb24gbGV2ZWxzLiBGb3IgdGhpcyBwdXJwb3NlLCB3ZSBwZXJmb3JtIFdlaWdodGVkIEdlbmUgTmV0d29yayBDb3JyZWxhdGlvbiBBbmFseXNpcyAoV0dDTkEpLiBUaGlzIGFsZ29yaXRobSBzZWFyY2hlcyBmb3IgYSBsYXRlbnQgc3RydWN0dXJlIHdpdGhpbiBmZWF0dXJlcywgaS5lIG1vZHVsZXMgb2YgY29ycmVsYXRlZCBnZW5lcyBhdCBwcm9tb3RlciBtZXRoeWxhdGlvbiBvciB0cmFuc2NyaXB0aW9uYWwgbGV2ZWxzIChbTGFuZ2ZlbGRlciAqZXQgYWwqLCAyMDA4XSgjbGFuZ2ZlbGRlcl8yMDA4KSkuDQoNCiMjIFBhcmFtZXRlciBzZXR0aW5ncyAmIFdHQ05BIGNvbXB1dGF0aW9uDQoNCkZpcnN0LCB3ZSBuZWVkIHRvIGRldGVybWluZSB0aGUgbW9zdCBzdWl0YWJsZSBXR0NOQSBwYXJhbWV0ZXJzIHRvIGFwcGx5IGZvciBlYWNoIGRhdGFzZXQuIFdlIG5vdGFibHkgbmVlZCB0byBjaG9vc2UgdGhlIHNvZnQtcG93ZXIgKHNvZnQgdGhyZXNob2xkaW5nIHBvd2VyKSB3aGljaCBpcyB1c2VkIHRvIHBvd2VyIHRoZSBhZGphY2VuY3kgbWF0cml4IG9mIGdlbmVzLCBpbiBvcmRlciB0byByZWR1Y2Ugc2lnbmFsIG5vaXNlLg0KDQpBY2NvcmRpbmcgdG8gW1dHQ05BIHBhY2thZ2UgZ3VpZGVsaW5lc10oaHR0cHM6Ly9ob3J2YXRoLmdlbmV0aWNzLnVjbGEuZWR1L2h0bWwvQ29leHByZXNzaW9uTmV0d29yay9ScGFja2FnZXMvV0dDTkEvZmFxLmh0bWwpLCBpdCBpcyBub3QgcmVjb21tZW5kZWQgZmlsdGVyaW5nIGRhdGEgcHJpb3IgdG8gV0dDTkEuIEluZGVlZCwgdGhpcyBpcyBkZXNpZ25lZCB0byBiZSBhbiB1bnN1cGVydmlzZWQgYW5hbHlzaXMgbWV0aG9kIHRoYXQgY2x1c3RlcnMgZ2VuZXMgYmFzZWQgb24gdGhlaXIgZXhwcmVzc2lvbiBwcm9maWxlcyAob3IgbWV0aHlsYXRpb24gcGF0dGVybnMsIGJ5IGV4dGVuc2lvbikuIEZvciBpbnN0YW5jZSwgZmlsdGVyaW5nIGdlbmVzIGJ5IGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHdvdWxkIGxlYWQgdG8gYSBzZXQgb2YgY29ycmVsYXRlZCBnZW5lcyB0aGF0IHdpbGwgZXNzZW50aWFsbHkgZm9ybSBhIGZldyBoaWdobHkgY29ycmVsYXRlZCBtb2R1bGVzLiBJdCB3b3VsZCBhbHNvIGNvbXBsZXRlbHkgaW52YWxpZGF0ZSB0aGUgc2NhbGUtZnJlZSB0b3BvbG9neSBhc3N1bXB0aW9uLCBzbyBjaG9vc2luZyBzb2Z0IHRocmVzaG9sZGluZyBwb3dlciBieSBzY2FsZS1mcmVlIHRvcG9sb2d5IGZpdCB3b3VsZCBmYWlsLiBUaGVyZWZvcmUsIHdlIHByb3ZpZGUgaW5wdXQgZGF0YSB0byB0aGUgV0dDTkEgYWxnb3JpdGhtLg0KDQpgYGBge3Igd2djbmEgZmEgZGV0ZXJtaW5lIHNvZnRwb3dlciwgZmlndXJlcy1zaWRlLCBmaWcuc2hvdz0iaG9sZCIsIG91dC53aWR0aD0iNTAlIixmaWcudG9wY2FwdGlvbiA9IFRSVUUgLCBmaWcuY2FwPSI8Y2VudGVyPioqU2V0dGluZyBwYXJhbWV0ZXJzIG9mIFdHQ05BKio8L2NlbnRlcj4ifQ0KDQojRm9yIFdHQ05BLCB3ZSB1c2UgdGhlIG5vcm1hbGl6ZWQgZXhwcmVzc2lvbiBtYXRyaXguDQoNCndnY25hX2V4cHJEYXQ9bGlzdChkbmFfdXBtX3N1YnNldCxybmFfc3Vic2V0KQ0KbmFtZXMod2djbmFfZXhwckRhdCk9YygiZG5hIiwicm5hIikNCg0Kd2djbmFfZGF0RXhwcj1sYXBwbHkod2djbmFfZXhwckRhdCx0KSAjV2UgdHJhbnNwb3NlIHRoZSBub3JtYWxpemVkIGV4cHJlc3Npb24gI21hdHJpeCwgYmVjYXVzZSBXR0NOQSBpcyB1c2VkIHRvIGZpbmQgY292YXJpYW5jZSBldHdlZW4gZ2VuZXMsIG5vdCBiZXR3ZWVuICNzYW1wbGUsIHVubGlrZSBQQ0EuDQoNCiMgQ2hvb3NlIGEgc2V0IG9mIHNvZnQtdGhyZXNob2xkaW5nIHBvd2Vycw0KcG93ZXJzID0gYygxOjIwKQ0KIyBDYWxsIHRoZSBuZXR3b3JrIHRvcG9sb2d5IGFuYWx5c2lzIGZ1bmN0aW9uDQpzZnQgPSBsYXBwbHkod2djbmFfZGF0RXhwcixwaWNrU29mdFRocmVzaG9sZCwgbmV0d29ya1R5cGUgPSAic2lnbmVkIiwgcG93ZXJWZWN0b3IgPSBwb3dlcnMsIHZlcmJvc2UgPSA1KQ0KDQojIFBsb3QgdGhlIHJlc3VsdHM6DQpwYXIobWZyb3cgPSBjKDEsIDIpKTsNCm9wdGlvbnMocmVwci5wbG90LndpZHRoID0gMTQsIHJlcHIucGxvdC5oZWlnaHQgPSAxMCk7DQoNCiMgU2NhbGUtZnJlZSB0b3BvbG9neSBmaXQgaW5kZXggYXMgYSBmdW5jdGlvbiBvZiB0aGUgc29mdC10aHJlc2hvbGRpbmcgcG93ZXINCnBsb3QubmV3KCkNCnBsb3Qoc2Z0W1siZG5hIl1dJGZpdEluZGljZXNbLDFdLCAtc2lnbihzZnRbWyJkbmEiXV0kZml0SW5kaWNlc1ssM10pKnNmdFtbImRuYSJdXSRmaXRJbmRpY2VzWywyXSwNCnhsYWIgPSAiU29mdCBUaHJlc2hvbGQgKHBvd2VyKSIsIHlsYWIgPSAiU2NhbGUgRnJlZSBUb3BvbG9neSBNb2RlbOKQow0KLOKGkkZpdCxzaWduZWQgUl4yIiwgdHlwZSA9ICJuIiwNCm1haW4gPSBwYXN0ZSgiU2NhbGUgaW5kZXBlbmRlbmNlIikpOw0KdGV4dChzZnRbWyJkbmEiXV0kZml0SW5kaWNlc1ssMV0sIC1zaWduKHNmdFtbImRuYSJdXSRmaXRJbmRpY2VzWywzXSkqc2Z0W1siZG5hIl1dJGZpdEluZGljZXNbLDJdLA0KbGFiZWxzID0gcG93ZXJzLCBjZXggPSAwLjksIGNvbCA9ICJyZWQiKTsNCiMgdGhpcyBsaW5lIGNvcnJlc3BvbmRzIHRvIHVzaW5nIGFuIFJeMiBjdXQtb2ZmIG9mIGgNCmFibGluZShoID0gMC45LCBjb2wgPSAicmVkIikNCiMgTWVhbiBjb25uZWN0aXZpdHkgYXMgYSBmdW5jdGlvbiBvZiB0aGUgc29mdC10aHJlc2hvbGRpbmcgcG93ZXINCnBsb3Qoc2Z0W1siZG5hIl1dJGZpdEluZGljZXNbLDFdLCBzZnRbWyJkbmEiXV0kZml0SW5kaWNlc1ssNV0sDQp4bGFiID0gIlNvZnQgVGhyZXNob2xkIChwb3dlcikiLCB5bGFiID0gIk1lYW4gQ29ubmVjdGl2aXR5IiwgdHlwZSA9ICJuIiwNCm1haW4gPSBwYXN0ZSgiTWVhbiBjb25uZWN0aXZpdHkiKSk7DQp0ZXh0KHNmdFtbImRuYSJdXSRmaXRJbmRpY2VzWywxXSwgc2Z0W1siZG5hIl1dJGZpdEluZGljZXNbLDVdLCBsYWJlbHMgPSBwb3dlcnMsIGNleCA9IDAuOSwgY29sID0icmVkIikNCg0KIyBTY2FsZS1mcmVlIHRvcG9sb2d5IGZpdCBpbmRleCBhcyBhIGZ1bmN0aW9uIG9mIHRoZSBzb2Z0LXRocmVzaG9sZGluZyBwb3dlcg0KcGxvdC5uZXcoKQ0KcGxvdChzZnRbWyJybmEiXV0kZml0SW5kaWNlc1ssMV0sIC1zaWduKHNmdFtbInJuYSJdXSRmaXRJbmRpY2VzWywzXSkqc2Z0W1sicm5hIl1dJGZpdEluZGljZXNbLDJdLA0KeGxhYiA9ICJTb2Z0IFRocmVzaG9sZCAocG93ZXIpIiwgeWxhYiA9ICJTY2FsZSBGcmVlIFRvcG9sb2d5IE1vZGVs4pCjDQos4oaSRml0LHNpZ25lZCBSXjIiLCB0eXBlID0gIm4iLA0KbWFpbiA9IHBhc3RlKCJTY2FsZSBpbmRlcGVuZGVuY2UiKSk7DQp0ZXh0KHNmdFtbInJuYSJdXSRmaXRJbmRpY2VzWywxXSwgLXNpZ24oc2Z0W1sicm5hIl1dJGZpdEluZGljZXNbLDNdKSpzZnRbWyJybmEiXV0kZml0SW5kaWNlc1ssMl0sDQpsYWJlbHMgPSBwb3dlcnMsIGNleCA9IDAuOSwgY29sID0gInJlZCIpOw0KIyB0aGlzIGxpbmUgY29ycmVzcG9uZHMgdG8gdXNpbmcgYW4gUl4yIGN1dC1vZmYgb2YgaA0KYWJsaW5lKGggPSAwLjksIGNvbCA9ICJyZWQiKQ0KIyBNZWFuIGNvbm5lY3Rpdml0eSBhcyBhIGZ1bmN0aW9uIG9mIHRoZSBzb2Z0LXRocmVzaG9sZGluZyBwb3dlcg0KcGxvdChzZnRbWyJybmEiXV0kZml0SW5kaWNlc1ssMV0sIHNmdFtbInJuYSJdXSRmaXRJbmRpY2VzWyw1XSwNCnhsYWIgPSAiU29mdCBUaHJlc2hvbGQgKHBvd2VyKSIsIHlsYWIgPSAiTWVhbiBDb25uZWN0aXZpdHkiLCB0eXBlID0gIm4iLA0KbWFpbiA9IHBhc3RlKCJNZWFuIGNvbm5lY3Rpdml0eSIpKTsNCnRleHQoc2Z0W1sicm5hIl1dJGZpdEluZGljZXNbLDFdLCBzZnRbWyJybmEiXV0kZml0SW5kaWNlc1ssNV0sIGxhYmVscyA9IHBvd2VycywgY2V4ID0gMC45LCBjb2wgPSJyZWQiKQ0KYGBgYA0KDQpXZSBjaG9vc2UgYSBzb2Z0LXBvd2VyIG9mIDEyIGZvciBib3RoIGRhdGFzZXRzLCBhcyB0aGlzIHlpZWxkcyBhID4gMC45IHNjYWxlLWZyZWUgdG9wb2xvZ3kgZml0dGluZyBpbmRleCAoUjxzdXA+Mjwvc3VwPikuIEludHVpdGl2ZWx5LCB3ZSB3b3VsZCBoYXZlIHJhdGhlciBjaG9zZW4gYSBzb2Z0LXBvd2VyIG9mIDQgZm9yIHRoZSBzY1BCQVQgYW5kIDcgZm9yIHRoZSBzY1JOQS1TZXEgZGF0YXNldHMsIGFzIHRoZXNlIGNvcnJlc3BvbmQgdG8gdGhlIGluZmxlY3Rpb24gcG9pbnRzIG9mIHRoZSBjdXJ2ZXMgdGhhdCBhbHNvIHlpZWxkIGEgPiAwLjkgUjxzdXA+Mjwvc3VwPi4gSG93ZXZlciwgaXQgaXMgcmVwb3J0ZWQgaW4gW1dHQ05BIHBhY2thZ2UgZ3VpZGVsaW5lc10oaHR0cHM6Ly9ob3J2YXRoLmdlbmV0aWNzLnVjbGEuZWR1L2h0bWwvQ29leHByZXNzaW9uTmV0d29yay9ScGFja2FnZXMvV0dDTkEvZmFxLmh0bWwpIHRoYXQgYSBtaW5pbXVtIHNvZnQtcG93ZXIgb2YgMTIgaXMgYWR2aXNlZCBmb3Igc2lnbmVkIG5ldHdvcmtzLiBUaGlzIHNvZnQtcG93ZXIgdGhyZXNob2xkaW5nIGFsbG93cyB0aGUgY29ycmVsYXRpb24gbWF4aW1pemF0aW9uIHdpdGggc2NhbGUtZnJlZSB0b3BvbG9neSBwcm9kdWNpbmcgbG93IG1lYW4gY29ubmVjdGl2aXR5Lg0KDQpPbmUgcGl0ZmFsbCBvZiBXR0NOQSBpcyBvdmVyZml0dGluZyB0aGUgbW9kZWwuIFRoaXMgY291bGQgaGFwcGVuIGJ5IHNldHRpbmcgc29mdC1wb3dlciB0b28gaGlnaC4NCg0KR2l2ZW4gdGhlIGhpZ2ggbnVtYmVyIG9mIGdlbmVzLCB3ZSBmaXggYSB0aHJlc2hvbGQgb2YgMTAwIGdlbmVzIHBlciBtb2R1bGUsIHRvIGF2b2lkIG92ZXItcmVzb2x1dGlvbiB5aWVsZGluZyBpbmZsYXRlZCBzbWFsbCBtb2R1bGVzLg0KDQpXR0NOQSBvbiBnZW5lIHByb21vdGVyIG1ldGh5bGF0aW9uIHByb2R1Y2VzIDUwIG1vZHVsZXMsIHdoaWxlIGl0IHlpZWxkcyAzOSBtb2R1bGVzIG9mIGNvcnJlbGF0ZWQgZ2VuZSBleHByZXNzaW9uLiBUaGUgZ3JleSBncm91cHMgY29ycmVzcG9uZCB0byBnZW5lcyB0aGF0IGhhdmUgbm90IGJlZW4gYXNzaWduZWQgdG8gYW55IG1vZHVsZS4NCg0KIyMgV0dDTkEgY2FsY3VsYXRpb24NCg0KYGBgYHtyIHdnY25hIGNvbXB1dGUsIGZpZ3VyZXMtc2lkZSwgZmlnLnNob3c9ImhvbGQiLCBvdXQud2lkdGg9IjUwJSIsZmlnLmhvbGQ9VFJVRX0NCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyN3Z2NuYSMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KIyBkbmFfbmV0ID0gYmxvY2t3aXNlTW9kdWxlcyh3Z2NuYV9kYXRFeHByW1siZG5hIl1dLCBwb3dlciA9IDEyLCBtaW5Nb2R1bGVTaXplID0gMzAsIG5ldHdvcmtUeXBlID0gInNpZ25lZCIsDQojICAgICAgICAgICAgICAgICAgICAgICAgIHJlYXNzaWduVGhyZXNob2xkID0gMWUtNiwgbWVyZ2VDdXRIZWlnaHQgPSAwLjI1LA0KIyAgICAgICAgICAgICAgICAgICAgICAgICBudW1lcmljTGFiZWxzID0gVFJVRSwgcGFtUmVzcGVjdHNEZW5kcm8gPSBGQUxTRSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgc2F2ZVRPTXMgPSBUUlVFLCBuVGhyZWFkcyA9IDEwLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICBzYXZlVE9NRmlsZUJhc2UgPSAiZG5hIiwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IDAsIGNvclR5cGU9InBlYXJzb24iKQ0KIyANCiMgcm5hX25ldCA9IGJsb2Nrd2lzZU1vZHVsZXMod2djbmFfZGF0RXhwcltbInJuYSJdXSwgcG93ZXIgPSAxMiwgbWluTW9kdWxlU2l6ZSA9IDMwLCBuZXR3b3JrVHlwZSA9ICJzaWduZWQiLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICByZWFzc2lnblRocmVzaG9sZCA9IDFlLTYsIG1lcmdlQ3V0SGVpZ2h0ID0gMC4yNSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgbnVtZXJpY0xhYmVscyA9IFRSVUUsIHBhbVJlc3BlY3RzRGVuZHJvID0gRkFMU0UsDQojICAgICAgICAgICAgICAgICAgICAgICAgIHNhdmVUT01zID0gVFJVRSwgblRocmVhZHMgPSAxMCwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgc2F2ZVRPTUZpbGVCYXNlID0gInJuYSIsDQojICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSAwLCBjb3JUeXBlPSJwZWFyc29uIikNCg0KIyBzYXZlLmltYWdlKCIyMDIxXzA2XzEyX2VtYnJ5b193Z2NuYV9jb21wdXRhdGlvbi5SRGF0YSIpDQpsb2FkKCIyMDIxXzA2XzEyX2VtYnJ5b193Z2NuYV9jb21wdXRhdGlvbi5SRGF0YSIpDQoNCiMgQ29udmVydCBsYWJlbHMgdG8gY29sb3JzIGZvciBwbG90dGluZw0KZG5hX21lcmdlZF9jb2xvcnMgPSBsYWJlbHMyY29sb3JzKGRuYV9uZXQkY29sb3JzKQ0Kcm5hX21lcmdlZF9jb2xvcnMgPSBsYWJlbHMyY29sb3JzKHJuYV9uZXQkY29sb3JzKQ0KIyBQbG90IHRoZSBkZW5kcm9ncmFtIGFuZCB0aGUgbW9kdWxlIGNvbG9ycyB1bmRlcm5lYXRoDQpwbG90Lm5ldygpDQpwbG90RGVuZHJvQW5kQ29sb3JzKGRuYV9uZXQkZGVuZHJvZ3JhbXNbWzFdXSwgZG5hX21lcmdlZF9jb2xvcnNbZG5hX25ldCRibG9ja0dlbmVzW1sxXV1dLA0KICAgICAgICAgICAgICAgICAgICAiRE5BIG1ldGh5bGF0aW9uIE1vZHVsZSBjb2xvcnMiLA0KICAgICAgICAgICAgICAgICAgICBkZW5kcm9MYWJlbHMgPSBGQUxTRSwgaGFuZyA9IDAuMDMsDQogICAgICAgICAgICAgICAgICAgIGFkZEd1aWRlID0gVFJVRSwgZ3VpZGVIYW5nID0gMC4wNSkNCg0KcGxvdERlbmRyb0FuZENvbG9ycyhybmFfbmV0JGRlbmRyb2dyYW1zW1sxXV0sIHJuYV9tZXJnZWRfY29sb3JzW3JuYV9uZXQkYmxvY2tHZW5lc1tbMV1dXSwNCiAgICAgICAgICAgICAgICAgICAgIlJOQSBtZXRoeWxhdGlvbiBNb2R1bGUgY29sb3JzIiwNCiAgICAgICAgICAgICAgICAgICAgZGVuZHJvTGFiZWxzID0gRkFMU0UsIGhhbmcgPSAwLjAzLA0KICAgICAgICAgICAgICAgICAgICBhZGRHdWlkZSA9IFRSVUUsIGd1aWRlSGFuZyA9IDAuMDUpDQpgYGBgDQoNCiMjIFZpc3VhbGl6YXRpb24gb2YgV0dDTkEgbmV0d29ya3MNCg0KSW5zdGVhZCBvZiBDeXRvc2NhcGUsIHdlIHVzZSBwbG90bHkgdG8gc2VlIGl0IGluIDNELiBXaGF0IGNvdWxkIGJlIHRoZSB0aGlyZCBkaW1lbnNpb24gPyBpbnRlcmFjdGlvbiB0b28gKD8pDQoNCiMjIEdlbmUgbW9kdWxlIG1lbWJlcnNoaXAgJiBjb25uZWN0aXZpdHkNCg0KYGBgYHtyIGNhbGN1bGF0aW5nIG1lbWJlcnNoaXAgYW5kIGNvbm5lY3Rpdml0eSBvZiBnZW5lcyB0byBXR0NOQSBtb2R1bGVzLCBlY2hvPUZBTFNFfQ0KDQojR2VuZSBwcm9tb3RlciBtb2R1bGUgY29ycmVzcG9uZGVuY2UNCnByb21vdGVyX25hbWVzIDwtIGNvbG5hbWVzKHdnY25hX2RhdEV4cHJbWyJkbmEiXV0pDQpwcm9tb3Rlcl9tb2R1bGVfbmFtZXMgPC1zdWJzdHIoY24oZG5hX25ldCRNRXMpLDMsMTApDQpwcm9tb3Rlcl9tb2R1bGVzPC1kYXRhLmZyYW1lKGdlbmVfcHJvbW90ZXI9Y29sbmFtZXMod2djbmFfZGF0RXhwcltbImRuYSJdXSksTW9kdWxlPWRuYV9uZXQkY29sb3JzKQ0KDQojTWVtYmVyc2hpcCBvZiBnZW5lX3Byb21vdGVycw0KcHJvbW90ZXJfbW9kdWxlX21lbWJlcnNoaXAgPC0gYXMuZGF0YS5mcmFtZShjb3Iod2djbmFfZGF0RXhwcltbImRuYSJdXSwgZG5hX25ldCRNRXMsIHVzZSA9ICJwIikpOw0KY29sbmFtZXMocHJvbW90ZXJfbW9kdWxlX21lbWJlcnNoaXApIDwtIHByb21vdGVyX21vZHVsZV9uYW1lczsNCg0KI0Nvbm5lY3Rpdml0eQ0KcHJvbW90ZXJfbW9kdWxlX2RlZ3JlZXM9aW50cmFtb2R1bGFyQ29ubmVjdGl2aXR5LmZyb21FeHByKHdnY25hX2RhdEV4cHJbWyJkbmEiXV0sIGRuYV9uZXQkY29sb3JzLG5ldHdvcmtUeXBlID0gInNpZ25lZCIscG93ZXI9MTIpI0luLWhvdXNlIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgY29ubmVjdGl2aXR5IG9mIGdlbmVfcHJvbW90ZXJzIHdpdGhpbiBXR0NOQSBtb2R1bGVzLiAxMiBpcyB0aGUgc29mdC1wb3dlciB3ZSBjaG9zZSBmb3IgV0dDTkEgbW9kdWxlIGNvbXB1dGF0aW9uDQpyb3duYW1lcyhwcm9tb3Rlcl9tb2R1bGVfZGVncmVlcyk8LXByb21vdGVyX25hbWVzDQoNCmdlbmVfcHJvbW90ZXJfbW9kdWxlX21lbWJlcnNoaXAgPC0gTlVMTA0KZ2VuZV9wcm9tb3Rlcl9tb2R1bGVfbmV3X25hbWU9TlVMTA0KDQpmb3IobW9kdWxlIGluIHByb21vdGVyX21vZHVsZV9uYW1lcyApew0KICBwcm9tb3RlcnM8LXByb21vdGVyX21vZHVsZXMkZ2VuZV9wcm9tb3Rlclt3aGljaChwcm9tb3Rlcl9tb2R1bGVzJE1vZHVsZT09bW9kdWxlKV0NCiAgZDwtZGF0YS5mcmFtZShyb3cubmFtZXMgPSBwcm9tb3RlcnMsIEludHJhQ29ubmVjdGl2aXR5PXByb21vdGVyX21vZHVsZV9kZWdyZWVzW3Byb21vdGVycywia1dpdGhpbiJdLA0KICAgICAgICAgICAgICAgIEludGVyQ29ubmVjdGl2aXR5PXByb21vdGVyX21vZHVsZV9kZWdyZWVzW3Byb21vdGVycywia091dCJdLE1lbWJlcnNoaXA9cHJvbW90ZXJfbW9kdWxlX21lbWJlcnNoaXBbcHJvbW90ZXJzLG1vZHVsZV0pDQogIGdlbmVfcHJvbW90ZXJfbW9kdWxlX21lbWJlcnNoaXBbW21vZHVsZV1dPC1kW29yZGVyKGQkTWVtYmVyc2hpcCxkZWNyZWFzaW5nID0gVFJVRSksXQ0KICBnZW5lX3Byb21vdGVyX21vZHVsZV9uZXdfbmFtZT1jKGdlbmVfcHJvbW90ZXJfbW9kdWxlX25ld19uYW1lLHJvd25hbWVzKGdlbmVfcHJvbW90ZXJfbW9kdWxlX21lbWJlcnNoaXBbW21vZHVsZV1dWzEsXSkpDQp9DQoNCmthYmxlKGhlYWQoZ2VuZV9wcm9tb3Rlcl9tb2R1bGVfbWVtYmVyc2hpcFtbMV1dLDUpLGNhcHRpb24gPSAiZ2VuZV9wcm9tb3RlciBtZW1iZXJzaGlwICYgY29ubmVjdGl2aXR5IG9mIFRUQzUgbW9kdWxlIikNCg0KDQojR2VuZSB0cmFuc2NyaXB0IG1vZHVsZSBjb3JyZXNwb25kZW5jZQ0KdHJhbnNjcmlwdF9uYW1lcyA8LSBjb2xuYW1lcyh3Z2NuYV9kYXRFeHByW1sicm5hIl1dKQ0KdHJhbnNjcmlwdF9tb2R1bGVfbmFtZXMgPC1zdWJzdHIoY24ocm5hX25ldCRNRXMpLDMsMTApDQp0cmFuc2NyaXB0X21vZHVsZXM8LWRhdGEuZnJhbWUoZ2VuZV90cmFuc2NyaXB0PWNvbG5hbWVzKHdnY25hX2RhdEV4cHJbWyJybmEiXV0pLE1vZHVsZT1ybmFfbmV0JGNvbG9ycykNCg0KI01lbWJlcnNoaXAgb2YgZ2VuZV90cmFuc2NyaXB0cw0KdHJhbnNjcmlwdF9tb2R1bGVfbWVtYmVyc2hpcCA8LSBhcy5kYXRhLmZyYW1lKGNvcih3Z2NuYV9kYXRFeHByW1sicm5hIl1dLCBybmFfbmV0JE1FcywgdXNlID0gInAiKSk7DQpjb2xuYW1lcyh0cmFuc2NyaXB0X21vZHVsZV9tZW1iZXJzaGlwKSA8LSB0cmFuc2NyaXB0X21vZHVsZV9uYW1lczsNCg0KI0Nvbm5lY3Rpdml0eQ0KdHJhbnNjcmlwdF9tb2R1bGVfZGVncmVlcz1pbnRyYW1vZHVsYXJDb25uZWN0aXZpdHkuZnJvbUV4cHIod2djbmFfZGF0RXhwcltbInJuYSJdXSwgcm5hX25ldCRjb2xvcnMsbmV0d29ya1R5cGUgPSAic2lnbmVkIixwb3dlcj0xMikjSW4taG91c2UgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRoZSBjb25uZWN0aXZpdHkgb2YgZ2VuZV90cmFuc2NyaXB0cyB3aXRoaW4gV0dDTkEgbW9kdWxlcy4gMTIgaXMgdGhlIHNvZnQtcG93ZXIgd2UgY2hvc2UgZm9yIFdHQ05BIG1vZHVsZSBjb21wdXRhdGlvbg0Kcm93bmFtZXModHJhbnNjcmlwdF9tb2R1bGVfZGVncmVlcyk8LXRyYW5zY3JpcHRfbmFtZXMNCg0KZ2VuZV90cmFuc2NyaXB0X21vZHVsZV9tZW1iZXJzaGlwIDwtIE5VTEwNCmdlbmVfdHJhbnNjcmlwdF9tb2R1bGVfbmV3X25hbWU9TlVMTA0KDQpmb3IobW9kdWxlIGluIHRyYW5zY3JpcHRfbW9kdWxlX25hbWVzICl7DQogIHRyYW5zY3JpcHRzPC10cmFuc2NyaXB0X21vZHVsZXMkZ2VuZV90cmFuc2NyaXB0W3doaWNoKHRyYW5zY3JpcHRfbW9kdWxlcyRNb2R1bGU9PW1vZHVsZSldDQogIGQ8LWRhdGEuZnJhbWUocm93Lm5hbWVzID0gdHJhbnNjcmlwdHMsIEludHJhQ29ubmVjdGl2aXR5PXRyYW5zY3JpcHRfbW9kdWxlX2RlZ3JlZXNbdHJhbnNjcmlwdHMsImtXaXRoaW4iXSwNCiAgICAgICAgICAgICAgICBJbnRlckNvbm5lY3Rpdml0eT10cmFuc2NyaXB0X21vZHVsZV9kZWdyZWVzW3RyYW5zY3JpcHRzLCJrT3V0Il0sTWVtYmVyc2hpcD10cmFuc2NyaXB0X21vZHVsZV9tZW1iZXJzaGlwW3RyYW5zY3JpcHRzLG1vZHVsZV0pDQogIGdlbmVfdHJhbnNjcmlwdF9tb2R1bGVfbWVtYmVyc2hpcFtbbW9kdWxlXV08LWRbb3JkZXIoZCRNZW1iZXJzaGlwLGRlY3JlYXNpbmcgPSBUUlVFKSxdDQogIGdlbmVfdHJhbnNjcmlwdF9tb2R1bGVfbmV3X25hbWU9YyhnZW5lX3RyYW5zY3JpcHRfbW9kdWxlX25ld19uYW1lLHJvd25hbWVzKGdlbmVfdHJhbnNjcmlwdF9tb2R1bGVfbWVtYmVyc2hpcFtbbW9kdWxlXV1bMSxdKSkNCn0NCg0Ka2FibGUoaGVhZChnZW5lX3RyYW5zY3JpcHRfbW9kdWxlX21lbWJlcnNoaXBbWzFdXSw1KSxjYXB0aW9uID0gImdlbmVfdHJhbnNjcmlwdCBtZW1iZXJzaGlwICYgY29ubmVjdGl2aXR5IG9mIEFUWE4xIG1vZHVsZSIpDQoNCmBgYGANCg0KV2UgY2FsY3VsYXRlIG1lbWJlcnNoaXAgYW5kIGNvbm5lY3Rpdml0eSBvZiBtb2R1bGUgZ2VuZXMgYW5kIHJlbmFtZSBXR0NOQSBtb2R1bGVzIHdpdGggZWlnZW5nZW5lcyBkZWZpbmVkIGJ5IHRoZSBoaWdoZXN0IG1lbWJlcnNoaXAgdG8gbW9kdWxlLg0KDQojIyBDb3JyZWxhdGlvbiBoZWF0bWFwIG9mIFdHQ05BIG1vZHVsZXMgJiBkYXkgb2YgdHJlYXRtZW50IA0KDQpgYGBge3Igd2djbmEgcGxvdCBsYWJlbGVkIGhlYXRtYXAsIGZpZ3VyZXMtc2lkZSwgZmlnLnNob3c9ImhvbGQiLCBvdXQud2lkdGg9IjkwJSIsIGZpZy50b3BjYXB0aW9uID0gVFJVRSAsIGZpZy5jYXA9IjxjZW50ZXI+KipXR0NOQSBtb2R1bGVzLXRyYWl0IGNvcnJlbGF0aW9uIGhlYXRtYXAqKjwvY2VudGVyPiJ9DQoNCiMjIEROQSBtZXRoeWxhdGlvbg0KZG5hX21vZHVsZUxhYmVscyA9IGRuYV9uZXQkY29sb3JzDQpkbmFfbW9kdWxlQ29sb3JzID0gbGFiZWxzMmNvbG9ycyhkbmFfbmV0JGNvbG9ycykNCmRuYV9NRXMgPSBkbmFfbmV0JE1FczsNCmRuYV9uZXRyZWUgPSBkbmFfbmV0JGRlbmRyb2dyYW1zW1sxXV07DQoNCiMgRGVmaW5lIG51bWJlcnMgb2YgZ2VuZXMgYW5kIHNhbXBsZXMNCmRuYV9uR2VuZXMgPSBuY29sKHdnY25hX2RhdEV4cHJbWyJkbmEiXV0pOw0KZG5hX25TYW1wbGVzID0gbnJvdyh3Z2NuYV9kYXRFeHByW1siZG5hIl1dKTsNCmRuYV9NRXNfZWlnZW5nZW5lcyA9IG1vZHVsZUVpZ2VuZ2VuZXMod2djbmFfZGF0RXhwcltbImRuYSJdXSwgZG5hX21vZHVsZUxhYmVscykkZWlnZW5nZW5lcw0KDQpkbmFfTUVzX2VpZ2VuZ2VuZXM9ZG5hX01Fc19laWdlbmdlbmVzWyxjb2xuYW1lcyhkbmFfTUVzKV0NCmNvbG5hbWVzKGRuYV9NRXNfZWlnZW5nZW5lcyk9cGFzdGUwKCJNRV8iLGdlbmVfcHJvbW90ZXJfbW9kdWxlX25ld19uYW1lKQ0KY29sbmFtZXMoZG5hX01Fc19laWdlbmdlbmVzKVtsZW5ndGgoY29sbmFtZXMoZG5hX01Fc19laWdlbmdlbmVzKSldPXBhc3RlMChjb2xuYW1lcyhkbmFfTUVzX2VpZ2VuZ2VuZXMpW2xlbmd0aChjb2xuYW1lcyhkbmFfTUVzX2VpZ2VuZ2VuZXMpKV0sIl9NRTAiKSNPbiBwcsOpY2lzZSBxdWVsIGVzdCBsZSBncm91cGUgIjAiLCBxdWkgY29ycmVzcG9uZCBhdXggZ8OobmVzIG4nYXBwYXJ0ZW5hbnQgw6AgYXVjdW4gbW9kdWxlLg0KDQpkbmFfbW9kdWxlVHJhaXRDb3IgPSBjb3IoZG5hX01Fc19laWdlbmdlbmVzLCB0cmFpdHMsIHVzZSA9ICJwIik7DQpkbmFfbW9kdWxlVHJhaXRQdmFsdWUgPSBjb3JQdmFsdWVTdHVkZW50KGRuYV9tb2R1bGVUcmFpdENvciwgZG5hX25TYW1wbGVzKTsNCg0KIyBXaWxsIGRpc3BsYXkgY29ycmVsYXRpb25zIGFuZCB0aGVpciBwLXZhbHVlcw0KZG5hX3Rlc3RNYXRyaXggPSAgcGFzdGUoc2lnbmlmKGRuYV9tb2R1bGVUcmFpdENvciwgMiksICJcbigiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnbmlmKGRuYV9tb2R1bGVUcmFpdFB2YWx1ZSwgMSksICIpIiwgc2VwID0gIiIpOw0KZGltKGRuYV90ZXN0TWF0cml4KSA9IGRpbShkbmFfbW9kdWxlVHJhaXRDb3IpDQpwYXIobWFyID0gYyg2LCA4LCAxLCAxKSk7DQoNCiMgSGVhdG1hcChkbmFfbW9kdWxlVHJhaXRDb3IsY2x1c3RlcmluZ19tZXRob2Rfcm93cz0id2FyZC5EMiIsY2x1c3RlcmluZ19tZXRob2RfY29sdW1ucz0id2FyZC5EMiIpDQoNCkhlYXRtYXAoZG5hX21vZHVsZVRyYWl0Q29yWyxjKCJkYXlENiIsImRheUQ4IiwiZGF5RDEwIiwiZGF5RDEyIiwibGluZWFnZUVwaSIsImxpbmVhZ2VQRSIsImxpbmVhZ2VURSIpXSxjbHVzdGVyaW5nX21ldGhvZF9yb3dzPSJ3YXJkLkQyIixjbHVzdGVyX2NvbHVtbnMgPSBGKQ0KDQpkbmFfbW9kdWxlVHJhaXRDb3JfbWFpbiA9IGNvcihkbmFfTUVzX2VpZ2VuZ2VuZXMsIHRyYWl0c19tYWluLCB1c2UgPSAicCIpOw0KZG5hX21vZHVsZVRyYWl0UHZhbHVlX21haW4gPSBjb3JQdmFsdWVTdHVkZW50KGRuYV9tb2R1bGVUcmFpdENvcl9tYWluLCBkbmFfblNhbXBsZXMpOw0KDQojIFdpbGwgZGlzcGxheSBjb3JyZWxhdGlvbnMgYW5kIHRoZWlyIHAtdmFsdWVzDQpkbmFfdGVzdE1hdHJpeCA9ICBwYXN0ZShzaWduaWYoZG5hX21vZHVsZVRyYWl0Q29yX21haW4sIDIpLCAiXG4oIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpZ25pZihkbmFfbW9kdWxlVHJhaXRQdmFsdWVfbWFpbiwgMSksICIpIiwgc2VwID0gIiIpOw0KZGltKGRuYV90ZXN0TWF0cml4KSA9IGRpbShkbmFfbW9kdWxlVHJhaXRDb3JfbWFpbikNCnBhcihtYXIgPSBjKDYsIDgsIDEsIDEpKTsNCg0KSGVhdG1hcChkbmFfbW9kdWxlVHJhaXRDb3JfbWFpbixjbHVzdGVyaW5nX21ldGhvZF9yb3dzPSJ3YXJkLkQyIixjbHVzdGVyaW5nX21ldGhvZF9jb2x1bW5zPSJ3YXJkLkQyIikNCg0KIyMgUk5BDQpybmFfbW9kdWxlTGFiZWxzID0gcm5hX25ldCRjb2xvcnMNCnJuYV9tb2R1bGVDb2xvcnMgPSBsYWJlbHMyY29sb3JzKHJuYV9uZXQkY29sb3JzKQ0Kcm5hX01FcyA9IHJuYV9uZXQkTUVzOw0Kcm5hX25ldHJlZSA9IHJuYV9uZXQkZGVuZHJvZ3JhbXNbWzFdXTsNCg0KIyBEZWZpbmUgbnVtYmVycyBvZiBnZW5lcyBhbmQgc2FtcGxlcw0Kcm5hX25HZW5lcyA9IG5jb2wod2djbmFfZGF0RXhwcltbInJuYSJdXSk7DQpybmFfblNhbXBsZXMgPSBucm93KHdnY25hX2RhdEV4cHJbWyJybmEiXV0pOw0Kcm5hX01Fc19laWdlbmdlbmVzID0gbW9kdWxlRWlnZW5nZW5lcyh3Z2NuYV9kYXRFeHByW1sicm5hIl1dLCBybmFfbW9kdWxlTGFiZWxzKSRlaWdlbmdlbmVzDQoNCnJuYV9NRXNfZWlnZW5nZW5lcz1ybmFfTUVzX2VpZ2VuZ2VuZXNbLGNvbG5hbWVzKHJuYV9NRXMpXQ0KY29sbmFtZXMocm5hX01Fc19laWdlbmdlbmVzKT1wYXN0ZTAoIk1FXyIsZ2VuZV90cmFuc2NyaXB0X21vZHVsZV9uZXdfbmFtZSkNCmNvbG5hbWVzKHJuYV9NRXNfZWlnZW5nZW5lcylbbGVuZ3RoKGNvbG5hbWVzKHJuYV9NRXNfZWlnZW5nZW5lcykpXT1wYXN0ZTAoY29sbmFtZXMocm5hX01Fc19laWdlbmdlbmVzKVtsZW5ndGgoY29sbmFtZXMocm5hX01Fc19laWdlbmdlbmVzKSldLCJfTUUwIikjT24gcHLDqWNpc2UgcXVlbCBlc3QgbGUgZ3JvdXBlICIwIiwgcXVpIGNvcnJlc3BvbmQgYXV4IGfDqG5lcyBuJ2FwcGFydGVuYW50IMOgIGF1Y3VuIG1vZHVsZS4NCg0Kcm5hX21vZHVsZVRyYWl0Q29yID0gY29yKHJuYV9NRXNfZWlnZW5nZW5lcywgdHJhaXRzLCB1c2UgPSAicCIpOw0Kcm5hX21vZHVsZVRyYWl0UHZhbHVlID0gY29yUHZhbHVlU3R1ZGVudChybmFfbW9kdWxlVHJhaXRDb3IsIHJuYV9uU2FtcGxlcyk7DQoNCiMgV2lsbCBkaXNwbGF5IGNvcnJlbGF0aW9ucyBhbmQgdGhlaXIgcC12YWx1ZXMNCnJuYV90ZXN0TWF0cml4ID0gIHBhc3RlKHNpZ25pZihybmFfbW9kdWxlVHJhaXRDb3IsIDIpLCAiXG4oIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpZ25pZihybmFfbW9kdWxlVHJhaXRQdmFsdWUsIDEpLCAiKSIsIHNlcCA9ICIiKTsNCmRpbShybmFfdGVzdE1hdHJpeCkgPSBkaW0ocm5hX21vZHVsZVRyYWl0Q29yKQ0KcGFyKG1hciA9IGMoNiwgOCwgMSwgMSkpOw0KDQojIEhlYXRtYXAocm5hX21vZHVsZVRyYWl0Q29yLGNsdXN0ZXJpbmdfbWV0aG9kX3Jvd3M9IndhcmQuRDIiLGNsdXN0ZXJpbmdfbWV0aG9kX2NvbHVtbnM9IndhcmQuRDIiKQ0KDQpIZWF0bWFwKHJuYV9tb2R1bGVUcmFpdENvclssYygiZGF5RDYiLCJkYXlEOCIsImRheUQxMCIsImRheUQxMiIsImxpbmVhZ2VFcGkiLCJsaW5lYWdlUEUiLCJsaW5lYWdlVEUiKV0sY2x1c3RlcmluZ19tZXRob2Rfcm93cz0id2FyZC5EMiIsY2x1c3Rlcl9jb2x1bW5zID0gRikNCg0Kcm5hX21vZHVsZVRyYWl0Q29yX21haW4gPSBjb3Iocm5hX01Fc19laWdlbmdlbmVzLCB0cmFpdHNfbWFpbiwgdXNlID0gInAiKTsNCnJuYV9tb2R1bGVUcmFpdFB2YWx1ZV9tYWluID0gY29yUHZhbHVlU3R1ZGVudChybmFfbW9kdWxlVHJhaXRDb3JfbWFpbiwgcm5hX25TYW1wbGVzKTsNCg0KIyBXaWxsIGRpc3BsYXkgY29ycmVsYXRpb25zIGFuZCB0aGVpciBwLXZhbHVlcw0Kcm5hX3Rlc3RNYXRyaXggPSAgcGFzdGUoc2lnbmlmKHJuYV9tb2R1bGVUcmFpdENvcl9tYWluLCAyKSwgIlxuKCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBzaWduaWYocm5hX21vZHVsZVRyYWl0UHZhbHVlX21haW4sIDEpLCAiKSIsIHNlcCA9ICIiKTsNCmRpbShybmFfdGVzdE1hdHJpeCkgPSBkaW0ocm5hX21vZHVsZVRyYWl0Q29yX21haW4pDQpwYXIobWFyID0gYyg2LCA4LCAxLCAxKSk7DQoNCkhlYXRtYXAocm5hX21vZHVsZVRyYWl0Q29yX21haW4sY2x1c3RlcmluZ19tZXRob2Rfcm93cz0id2FyZC5EMiIsY2x1c3RlcmluZ19tZXRob2RfY29sdW1ucz0id2FyZC5EMiIpDQoNCmBgYGANCg0KV2Ugb2JzZXJ2ZSB0aGF0IG1vZHVsZXMgb2YgZ2VuZSBwcm9tb3RlcnMgYW5kIHRyYW5zY3JpcHRzIGNvcnJlbGF0ZSBuaWNlbHkgd2l0aCBkZXZlbG9wbWVudCBzdGFnZSBvciBjZWxsIGxpbmVhZ2UsIHNob3dpbmcgYWxtb3N0IGV4Y2x1c2l2ZSBwYXR0ZXJucy4gDQoNCiMgSW50ZWdyYXRpb24gb2YgY29ycmVsYXRlZCBnZW5lIGFuZCBETkEgbWV0aHlsYXRpb24gbW9kdWxlcw0KDQpXZSBub3cgcGVyZm9ybSBhbiBpbnRlZ3JhdGl2ZSBhcHByb2FjaCBvbiBjb3JyZWxhdGlvbiBuZXR3b3JrcyBiZXR3ZWVuIHRoZSB0d28gZGF0YXNldHMsIHRvIGludmVzdGlnYXRlIHBvdGVudGlhbCBpbnRlcmNvbm5lY3Rpb25zIGJldHdlZW4gZ2VuZXMgYW5kIEROQSBtZXRoeWxhdGlvbiAgbW9kdWxlcy4NCg0KYGBgYHtyIHdnY25hIDIgbmV0d29ya3MgY29ycmVsYXRpb24sIGZpZ3VyZXMtc2lkZSwgZmlnLnNob3c9ImhvbGQiLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MjAsIG91dC53aWR0aD0iOTAlIiwgZmlnLnRvcGNhcHRpb24gPSBUUlVFICwgZmlnLmNhcD0iPGNlbnRlcj4qKk11bHRpLU9taWNzIFdHQ05BIG1vZHVsZXMgY29ycmVsYXRpb24qKjwvY2VudGVyPiJ9DQoNCmRuYV9ybmFfbW9kdWxlVHJhaXRDb3JfbWFpbiA9IGNvcihybmFfTUVzX2VpZ2VuZ2VuZXMsIGRuYV9NRXNfZWlnZW5nZW5lcywgdXNlID0gInAiKTsNCmRuYV9ybmFfbW9kdWxlVHJhaXRQdmFsdWVfbWFpbiA9IGNvclB2YWx1ZVN0dWRlbnQoZG5hX3JuYV9tb2R1bGVUcmFpdENvcl9tYWluLCBkbmFfblNhbXBsZXMpOw0KDQojIFdpbGwgZGlzcGxheSBjb3JyZWxhdGlvbnMgYW5kIHRoZWlyIHAtdmFsdWVzDQpkbmFfcm5hX3Rlc3RNYXRyaXggPSAgcGFzdGUoc2lnbmlmKGRuYV9ybmFfbW9kdWxlVHJhaXRDb3JfbWFpbiwgMiksICJcbigiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnbmlmKGRuYV9ybmFfbW9kdWxlVHJhaXRQdmFsdWVfbWFpbiwgMSksICIpIiwgc2VwID0gIiIpOw0KZGltKGRuYV9ybmFfdGVzdE1hdHJpeCkgPSBkaW0oZG5hX3JuYV9tb2R1bGVUcmFpdENvcl9tYWluKQ0KcGFyKG1hciA9IGMoNiwgOCwgMSwgMSkpOw0KDQpIZWF0bWFwKGRuYV9ybmFfbW9kdWxlVHJhaXRDb3JfbWFpbixjbHVzdGVyaW5nX21ldGhvZF9yb3dzPSJ3YXJkLkQyIixjbHVzdGVyaW5nX21ldGhvZF9jb2x1bW5zPSJ3YXJkLkQyIixuYW1lID0gIkROQS1STkEgTW9kdWxlXG5jb3JyZWxhdGlvbiIscm93X3RpdGxlPSJXR0NOQSBtb2R1bGVzIGZvciBnZW5lIGV4cHJlc3Npb24iLCByb3dfdGl0bGVfc2lkZSA9ICJyaWdodCIsIHJvd190aXRsZV9ncCA9IGdwYXIoZm9udHNpemUgPSAxMy4yLCBmb250ZmFjZT0iYm9sZCIpLCByb3dfbmFtZXNfZ3AgPSBncGFyKGNvbCA9IGMoInB1cnBsZSIpKSwgY29sdW1uX3RpdGxlPSJXR0NOQSBtb2R1bGVzIGZvciBnZW5lIHByb21vdGVyIG1ldGh5bGF0aW9uIiwgY29sdW1uX3RpdGxlX3NpZGUgPSAiYm90dG9tIixjb2x1bW5fdGl0bGVfZ3AgPSBncGFyKGZvbnRzaXplID0gMTMuMiwgZm9udGZhY2U9ImJvbGQiKSwgY29sdW1uX25hbWVzX2dwID0gZ3Bhcihjb2wgPSBjKCIjMTNkMzcwIikpKQ0KDQpkbmFfcm5hX21vZHVsZV9jb3JyZWxhdGlvbl9wb2ludHM9TlVMTA0KbmFtZXNfZG5hX3JuYV9tb2R1bGVzPU5VTEwNCg0KZm9yIChpIGluIHNlcShucm93KGRuYV9ybmFfbW9kdWxlVHJhaXRDb3JfbWFpbikpKXsNCiAgDQogIGRuYV9ybmFfbW9kdWxlX2NvcnJlbGF0aW9uX3BvaW50cz1jKGRuYV9ybmFfbW9kdWxlX2NvcnJlbGF0aW9uX3BvaW50cyxkbmFfcm5hX21vZHVsZVRyYWl0Q29yX21haW5baSxdKQ0KICANCiAgbmFtZXNfZG5hX3JuYV9tb2R1bGVzPWMobmFtZXNfZG5hX3JuYV9tb2R1bGVzLHBhc3RlMChyZXAocm93bmFtZXMoZG5hX3JuYV9tb2R1bGVUcmFpdENvcl9tYWluKVtpXSxuY29sKGRuYV9ybmFfbW9kdWxlVHJhaXRDb3JfbWFpbikpLCItIixjb2xuYW1lcyhkbmFfcm5hX21vZHVsZVRyYWl0Q29yX21haW4pKSkNCiAgDQp9DQoNCmRuYV9ybmFfbW9kdWxlX2NvcnJlbGF0aW9uX3Bsb3Q9TlVMTA0KZG5hX3JuYV9tb2R1bGVfY29ycmVsYXRpb25fcGxvdCRtb2R1bGVfcGFpcj1uYW1lc19kbmFfcm5hX21vZHVsZXMNCmRuYV9ybmFfbW9kdWxlX2NvcnJlbGF0aW9uX3Bsb3QkY29yX3ZhbHVlPWRuYV9ybmFfbW9kdWxlX2NvcnJlbGF0aW9uX3BvaW50cw0KZG5hX3JuYV9tb2R1bGVfY29ycmVsYXRpb25fcGxvdD1hcy5kYXRhLmZyYW1lKGRuYV9ybmFfbW9kdWxlX2NvcnJlbGF0aW9uX3Bsb3QpDQoNCmdncGxvdChkbmFfcm5hX21vZHVsZV9jb3JyZWxhdGlvbl9wbG90LGFlcyh4PW1vZHVsZV9wYWlyLCB5PWNvcl92YWx1ZSwgbGFiZWw9bW9kdWxlX3BhaXIpKSsNCiAgZ2VvbV9wb2ludCgpKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsPWlmZWxzZShjb3JfdmFsdWUgPiAwLjUsbW9kdWxlX3BhaXIsJycpKSxoanVzdD0wLHZqdXN0PTApKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MC41LCBsaW5ldHlwZT0ic29saWQiLCANCiAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBzaXplPTEpDQoNCmBgYGANCg0KDQpXZSBzZWxlY3QgdGhlIHRvcCBtb3N0IGNvcnJlbGF0ZWQgb3IgYW50aS1jb3JyZWxhdGVkIGdlbmUgcHJvbW90ZXIgYW5kIHRyYW5zY3JpcHQgbW9kdWxlcyBmb3IgZnVydGhlciBpbnZlc3RpZ2F0aW9uLiBXZSBmb3VjcyBvbiAzIHBhaXJzIG9mIHRvcCBjb3JyZWxhdGVkIHRyYW5zY3JpcHQtcG9tb3RlciBtb2R1bGVzLCB0aGF0IGNoYXJhY3Rlcml6ZSBzb21lIGxpbmVhZ2Utc3RhZ2UgdHJhaXRzOg0KDQotIE1FX1JQUzIzLU1FX1NOT1JEMTE2LjggKEVwaS1kYXk2KQ0KLSBNRV9WV0NFLU1FX1NQQUNBNUIgKFBFLWRheTgpDQotIE1FX1VCRTJFMi1NRV9NWEkxIChURS1kYXk4KQ0KDQpjby1leHByZXNzaW9uIHNpbWlsYXJpdHkgYW5kICJjby1tZXRoeWxhdGlvbiBzaW1pbGFyaXR5Ig0KDQojIyBWaXN1YWxpemF0aW9uIG9mIFdHQ05BIG1vZHVsZSBuZXR3b3JrIHdpdGggQ3l0b3NjYXBlDQoNCiMjIEZ1bmN0aW9uYWwgZW5yaWNobWVudCBvbiBXR0NOQSBzZWxlY3RlZCBtb2R1bGVzDQoNCldlIHNldCBhIHAtdmFsdWUgdGhyZXNob2xkIG9mIDAuMSBmb3Igc2lnbmlmaWNhbnQgcHJvY2VzcyBlbnJpY2htZW50LCBhcyBtb2R1bGVzIH4gNDAgZ2VuZXMgZmFpbHMgdG8gcHJvZHVjZSBzaWduaWZpY2FudCByZXN1bHRzIHVuZGVyIGEgMC4wNSB0aHJlc2hvbGQuDQoNCmBgYGB7ciBHTyBhbmFseXNpcyBybmEgYW5kIGRuYSB3aXRoIGdvc3QsIGZpZy50b3BjYXB0aW9uID0gVFJVRSAsIGZpZy5jYXA9IjxjZW50ZXI+KipHZW5lIE9udG9sb2d5IGFuYWx5c2lzIG9mIFdHQ05BIG1vZHVsZSBnZW5lIHRyYW5zY3JpcHRzIGFuZCBnZW5lIHByb21vdGVycyoqPC9jZW50ZXI+IiwgZmlnLmhvbGQ9VFJVRX0NCiMgV2UgZ2V0IHRoZSBnZW5lcyBmcm9tIHNlbGVjdGVkIFdHQ05BIG1vZHVsZXMNCmdlbmVfcHJvbW90ZXJfbW9kdWxlX21lbWJlcnNoaXBfZ289Z2VuZV9wcm9tb3Rlcl9tb2R1bGVfbWVtYmVyc2hpcA0KbmFtZXMoZ2VuZV9wcm9tb3Rlcl9tb2R1bGVfbWVtYmVyc2hpcF9nbyk9Z2VuZV9wcm9tb3Rlcl9tb2R1bGVfbmV3X25hbWUNCg0KZ2VuZV90cmFuc2NyaXB0X21vZHVsZV9tZW1iZXJzaGlwX2dvPWdlbmVfdHJhbnNjcmlwdF9tb2R1bGVfbWVtYmVyc2hpcA0KbmFtZXMoZ2VuZV90cmFuc2NyaXB0X21vZHVsZV9tZW1iZXJzaGlwX2dvKT1nZW5lX3RyYW5zY3JpcHRfbW9kdWxlX25ld19uYW1lDQoNCm1vZHVsZV9wcm9tb3RlcnNfZXBpX2Q2IDwtIHJvd25hbWVzKGdlbmVfcHJvbW90ZXJfbW9kdWxlX21lbWJlcnNoaXBfZ29bWyJTVEFSRDEwIl1dKQ0KbW9kdWxlX3RyYW5zY3JpcHRzX2VwaV9kNiA8LSByb3duYW1lcyhnZW5lX3RyYW5zY3JpcHRfbW9kdWxlX21lbWJlcnNoaXBfZ29bWyJSUDlQIl1dKQ0KDQoNCm1vZHVsZV9wcm9tb3RlcnNfcGVfZDggPC0gcm93bmFtZXMoZ2VuZV9wcm9tb3Rlcl9tb2R1bGVfbWVtYmVyc2hpcF9nb1tbIlNQQUNBNUIiXV0pDQptb2R1bGVfdHJhbnNjcmlwdHNfcGVfZDggPC0gcm93bmFtZXMoZ2VuZV90cmFuc2NyaXB0X21vZHVsZV9tZW1iZXJzaGlwX2dvW1siVldDRSJdXSkNCg0KbW9kdWxlX3Byb21vdGVyc190ZV9kNiA8LSByb3duYW1lcyhnZW5lX3Byb21vdGVyX21vZHVsZV9tZW1iZXJzaGlwX2dvW1siU05PUkQ3NiJdXSkNCm1vZHVsZV90cmFuc2NyaXB0c190ZV9kNiA8LSByb3duYW1lcyhnZW5lX3RyYW5zY3JpcHRfbW9kdWxlX21lbWJlcnNoaXBfZ29bWyJXTlQ3QiJdXSkNCg0KYGBgYA0KDQojIE5ldHdvcmsgdmlzdWFsaXphdGlvbg0KDQpBcyB0aGV5IGFyZSB0aGUgbWFpbiBkcml2ZXJzIG9mIGNlbGwgZmF0ZSBwcm9ncmVzc2lvbiwgd2UgZm9jdXMgb24gdHJhbnNjcmlwdGlvbiBmYWN0b3IgaW50ZXJhY3Rpb25zLiBXZSBxdWVyeSB0aGUgW1RGMkROQSBkYXRhYmFzZV0oaHR0cDovL2Zpc2VybGFiLm9yZy90ZjJkbmFfZGIvL2luZGV4Lmh0bWwpIG9uIGNvbXB1dGVkIGludGVyYWN0aW9ucyBpbiBodW1hbiBiYXNlZCBvbiBETkEgYmluZGluZyBtb3RpdmVzLiBUcmFuc2NyaXB0aW9uIGZhY3RvcnMgYXJlIHByZWNlZGVkIHdpdGggdGhlICJURiIgc3ltYm9sIGFuZCB5aWVsZCBhIGxpc3Qgb2YgcmVndWxhdGVkIGdlbmVzIHRoZXkgaW50ZXJhY3Qgd2l0aCwgd2hpbGUgb3RoZXIgY2xhc3NlcyBvZiBnZW5lcyBkbyBub3QuDQoNCmBgYGB7ciBxdWVyeSBtdWx0aXBsZSBnZW5lcyB0byBTVFJJTkcgaW50ZXJhY3Rpb24gZGF0YWJhc2V9DQoNCiMgZmFzdFdyaXRlKGMobW9kdWxlX3Byb21vdGVyc19lcGlfZDYsbW9kdWxlX3RyYW5zY3JpcHRzX2VwaV9kNiksZmlsZT0ibW9kdWxlX3Byb21vdGVyc19tb2R1bGVfdHJhbnNjcmlwdHNfZXBpX2Q2LnR4dCIsY29sLm5hbWVzID0gRikNCiMgZmFzdFdyaXRlKGMobW9kdWxlX3Byb21vdGVyc19wZV9kOCxtb2R1bGVfdHJhbnNjcmlwdHNfcGVfZDgpLGZpbGU9Im1vZHVsZV9wcm9tb3RlcnNfbW9kdWxlX3RyYW5zY3JpcHRzX3BlX2Q4LnR4dCIsY29sLm5hbWVzID0gRikNCiMgZmFzdFdyaXRlKGMobW9kdWxlX3Byb21vdGVyc190ZV9kNixtb2R1bGVfdHJhbnNjcmlwdHNfdGVfZDYpLGZpbGU9Im1vZHVsZV9wcm9tb3RlcnNfbW9kdWxlX3RyYW5zY3JpcHRzX3RlX2Q2LnR4dCIsY29sLm5hbWVzID0gRikNCg0KZXBpX2ludGVyYWN0aW9uX2ZpbGVzID0gbGlzdC5maWxlcyhwYXR0ZXJuPSJeZXBpX2Q2XyoiKQ0Kc3ViMT1zdWIoIl5bZXBpX2Q2X10rKCopIiwgIlxcMSIsIGVwaV9pbnRlcmFjdGlvbl9maWxlcykNCnN1YjI9c3ViKCJcXC5jc3YuKiIsICIiLCBzdWIxKQ0KZXBpX21vZHVsZV9pbnRlcmFjdGlvbnMgPSBsYXBwbHkoZXBpX2ludGVyYWN0aW9uX2ZpbGVzLCBmYXN0UmVhZDIpDQpuYW1lcyhlcGlfbW9kdWxlX2ludGVyYWN0aW9ucyk9c3ViMg0KDQpwZV9pbnRlcmFjdGlvbl9maWxlcyA9IGxpc3QuZmlsZXMocGF0dGVybj0iXnBlX2Q4XyoiKQ0Kc3ViMT1zdWIoIl5bcGVfZDhfXSsoKikiLCAiXFwxIiwgcGVfaW50ZXJhY3Rpb25fZmlsZXMpDQpzdWIyPXN1YigiXFwuY3N2LioiLCAiIiwgc3ViMSkNCnBlX21vZHVsZV9pbnRlcmFjdGlvbnMgPSBsYXBwbHkocGVfaW50ZXJhY3Rpb25fZmlsZXMsIGZhc3RSZWFkMikNCm5hbWVzKHBlX21vZHVsZV9pbnRlcmFjdGlvbnMpPXN1YjINCg0KdGVfaW50ZXJhY3Rpb25fZmlsZXMgPSBsaXN0LmZpbGVzKHBhdHRlcm49Il50ZV9kNl8qIikNCnN1YjE9c3ViKCJeW3RlX2Q2X10rKCopIiwgIlxcMSIsIHRlX2ludGVyYWN0aW9uX2ZpbGVzKQ0Kc3ViMj1zdWIoIlxcLmNzdi4qIiwgIiIsIHN1YjEpDQp0ZV9tb2R1bGVfaW50ZXJhY3Rpb25zID0gbGFwcGx5KHRlX2ludGVyYWN0aW9uX2ZpbGVzLCBmYXN0UmVhZDIpDQpuYW1lcyh0ZV9tb2R1bGVfaW50ZXJhY3Rpb25zKT1zdWIyDQoNCmVwaV9kNl9kbmFfcm5hX21vZHVsZV9pbnRlcmFjdGlvbj1OVUxMDQplcGlfZDZfaW50ZXJhY3Rpb25fZGY9TlVMTA0KZm9yIChpIGluIG5hbWVzKGVwaV9tb2R1bGVfaW50ZXJhY3Rpb25zKSl7DQogIGVwaV9kNl9kbmFfcm5hX21vZHVsZV9pbnRlcmFjdGlvbltbaV1dPWNiaW5kKHNvdXJjZV9ub2RlPXJlcChuYW1lcyhlcGlfbW9kdWxlX2ludGVyYWN0aW9uc1tpXSksbnJvdyhlcGlfbW9kdWxlX2ludGVyYWN0aW9uc1tbaV1dKSksdGFyZ2V0X25vZGU9cm93bmFtZXMoZXBpX21vZHVsZV9pbnRlcmFjdGlvbnNbW2ldXSkscF92YWx1ZT1lcGlfbW9kdWxlX2ludGVyYWN0aW9uc1tbaV1dJHBfdmFsdWUsY2hyb21vc29tZT1lcGlfbW9kdWxlX2ludGVyYWN0aW9uc1tbaV1dJGNocm9tb3NvbWVfbmFtZSxkb3duc3RyZWFtX2dlbmU9ZXBpX21vZHVsZV9pbnRlcmFjdGlvbnNbW2ldXSRkb3duc3RyZWFtKQ0KDQogIGVwaV9kNl9pbnRlcmFjdGlvbl9kZj1yYmluZChlcGlfZDZfaW50ZXJhY3Rpb25fZGYsZXBpX2Q2X2RuYV9ybmFfbW9kdWxlX2ludGVyYWN0aW9uW1tpXV0pDQp9DQoNCnBlX2Q4X2RuYV9ybmFfbW9kdWxlX2ludGVyYWN0aW9uPU5VTEwNCnBlX2Q4X2ludGVyYWN0aW9uX2RmPU5VTEwNCmZvciAoaSBpbiBuYW1lcyhwZV9tb2R1bGVfaW50ZXJhY3Rpb25zKSl7DQogIHBlX2Q4X2RuYV9ybmFfbW9kdWxlX2ludGVyYWN0aW9uW1tpXV09Y2JpbmQoc291cmNlX25vZGU9cmVwKG5hbWVzKHBlX21vZHVsZV9pbnRlcmFjdGlvbnNbaV0pLG5yb3cocGVfbW9kdWxlX2ludGVyYWN0aW9uc1tbaV1dKSksdGFyZ2V0X25vZGU9cm93bmFtZXMocGVfbW9kdWxlX2ludGVyYWN0aW9uc1tbaV1dKSxwX3ZhbHVlPXBlX21vZHVsZV9pbnRlcmFjdGlvbnNbW2ldXSRwX3ZhbHVlLGNocm9tb3NvbWU9cGVfbW9kdWxlX2ludGVyYWN0aW9uc1tbaV1dJGNocm9tb3NvbWVfbmFtZSxkb3duc3RyZWFtX2dlbmU9cGVfbW9kdWxlX2ludGVyYWN0aW9uc1tbaV1dJGRvd25zdHJlYW0pDQoNCiAgcGVfZDhfaW50ZXJhY3Rpb25fZGY9cmJpbmQocGVfZDhfaW50ZXJhY3Rpb25fZGYscGVfZDhfZG5hX3JuYV9tb2R1bGVfaW50ZXJhY3Rpb25bW2ldXSkNCn0NCg0KdGVfZDZfZG5hX3JuYV9tb2R1bGVfaW50ZXJhY3Rpb249TlVMTA0KdGVfZDZfaW50ZXJhY3Rpb25fZGY9TlVMTA0KZm9yIChpIGluIG5hbWVzKHRlX21vZHVsZV9pbnRlcmFjdGlvbnMpKXsNCiAgdGVfZDZfZG5hX3JuYV9tb2R1bGVfaW50ZXJhY3Rpb25bW2ldXT1jYmluZChzb3VyY2Vfbm9kZT1yZXAobmFtZXModGVfbW9kdWxlX2ludGVyYWN0aW9uc1tpXSksbnJvdyh0ZV9tb2R1bGVfaW50ZXJhY3Rpb25zW1tpXV0pKSx0YXJnZXRfbm9kZT1yb3duYW1lcyh0ZV9tb2R1bGVfaW50ZXJhY3Rpb25zW1tpXV0pLHBfdmFsdWU9dGVfbW9kdWxlX2ludGVyYWN0aW9uc1tbaV1dJHBfdmFsdWUsY2hyb21vc29tZT10ZV9tb2R1bGVfaW50ZXJhY3Rpb25zW1tpXV0kY2hyb21vc29tZV9uYW1lLGRvd25zdHJlYW1fZ2VuZT10ZV9tb2R1bGVfaW50ZXJhY3Rpb25zW1tpXV0kZG93bnN0cmVhbSkNCg0KICB0ZV9kNl9pbnRlcmFjdGlvbl9kZj1yYmluZCh0ZV9kNl9pbnRlcmFjdGlvbl9kZix0ZV9kNl9kbmFfcm5hX21vZHVsZV9pbnRlcmFjdGlvbltbaV1dKQ0KfQ0KDQplcGlfZDZfZG5hX2FkamFjZW5jeSA8LSBhZGphY2VuY3kod2djbmFfZGF0RXhwcltbImRuYSJdXSwNCnNlbGVjdENvbHMgPSBOVUxMLA0KdHlwZSA9ICJzaWduZWQiLA0KcG93ZXIgPSAxMiwgI3NldCBzb2Z0LXBvd2VyIHRvIDEyIGFzIGZvciBtb2R1bGUgY29tcHV0YXRpb24NCmNvckZuYyA9ICJjb3IiLCBjb3JPcHRpb25zID0gbGlzdCh1c2UgPSAicCIpLA0Kd2VpZ2h0cyA9IE5VTEwsDQpkaXN0Rm5jID0gImRpc3QiLCBkaXN0T3B0aW9ucyA9ICJtZXRob2QgPSAnd2FyZC5EMiciLA0Kd2VpZ2h0QXJnTmFtZXMgPSBjKCJ3ZWlnaHRzLngiLCAid2VpZ2h0cy55IikpDQoNCmVwaV9kNl9ybmFfYWRqYWNlbmN5IDwtIGFkamFjZW5jeSh3Z2NuYV9kYXRFeHByW1sicm5hIl1dLA0Kc2VsZWN0Q29scyA9IE5VTEwsDQp0eXBlID0gInNpZ25lZCIsDQpwb3dlciA9IDEyLCAjc2V0IHNvZnQtcG93ZXIgdG8gMTIgYXMgZm9yIG1vZHVsZSBjb21wdXRhdGlvbg0KY29yRm5jID0gImNvciIsIGNvck9wdGlvbnMgPSBsaXN0KHVzZSA9ICJwIiksDQp3ZWlnaHRzID0gTlVMTCwNCmRpc3RGbmMgPSAiZGlzdCIsIGRpc3RPcHRpb25zID0gIm1ldGhvZCA9ICd3YXJkLkQyJyIsDQp3ZWlnaHRBcmdOYW1lcyA9IGMoIndlaWdodHMueCIsICJ3ZWlnaHRzLnkiKSkNCg0KcGVfZDhfZG5hX2FkamFjZW5jeSA8LSBhZGphY2VuY3kod2djbmFfZGF0RXhwcltbImRuYSJdXSwNCnNlbGVjdENvbHMgPSBOVUxMLA0KdHlwZSA9ICJzaWduZWQiLA0KcG93ZXIgPSAxMiwgI3NldCBzb2Z0LXBvd2VyIHRvIDEyIGFzIGZvciBtb2R1bGUgY29tcHV0YXRpb24NCmNvckZuYyA9ICJjb3IiLCBjb3JPcHRpb25zID0gbGlzdCh1c2UgPSAicCIpLA0Kd2VpZ2h0cyA9IE5VTEwsDQpkaXN0Rm5jID0gImRpc3QiLCBkaXN0T3B0aW9ucyA9ICJtZXRob2QgPSAnd2FyZC5EMiciLA0Kd2VpZ2h0QXJnTmFtZXMgPSBjKCJ3ZWlnaHRzLngiLCAid2VpZ2h0cy55IikpDQoNCnBlX2Q4X3JuYV9hZGphY2VuY3kgPC0gYWRqYWNlbmN5KHdnY25hX2RhdEV4cHJbWyJybmEiXV0sDQpzZWxlY3RDb2xzID0gTlVMTCwNCnR5cGUgPSAic2lnbmVkIiwNCnBvd2VyID0gMTIsICNzZXQgc29mdC1wb3dlciB0byAxMiBhcyBmb3IgbW9kdWxlIGNvbXB1dGF0aW9uDQpjb3JGbmMgPSAiY29yIiwgY29yT3B0aW9ucyA9IGxpc3QodXNlID0gInAiKSwNCndlaWdodHMgPSBOVUxMLA0KZGlzdEZuYyA9ICJkaXN0IiwgZGlzdE9wdGlvbnMgPSAibWV0aG9kID0gJ3dhcmQuRDInIiwNCndlaWdodEFyZ05hbWVzID0gYygid2VpZ2h0cy54IiwgIndlaWdodHMueSIpKQ0KDQp0ZV9kNl9kbmFfYWRqYWNlbmN5IDwtIGFkamFjZW5jeSh3Z2NuYV9kYXRFeHByW1siZG5hIl1dLA0Kc2VsZWN0Q29scyA9IE5VTEwsDQp0eXBlID0gInNpZ25lZCIsDQpwb3dlciA9IDEyLCAjc2V0IHNvZnQtcG93ZXIgdG8gMTIgYXMgZm9yIG1vZHVsZSBjb21wdXRhdGlvbg0KY29yRm5jID0gImNvciIsIGNvck9wdGlvbnMgPSBsaXN0KHVzZSA9ICJwIiksDQp3ZWlnaHRzID0gTlVMTCwNCmRpc3RGbmMgPSAiZGlzdCIsIGRpc3RPcHRpb25zID0gIm1ldGhvZCA9ICd3YXJkLkQyJyIsDQp3ZWlnaHRBcmdOYW1lcyA9IGMoIndlaWdodHMueCIsICJ3ZWlnaHRzLnkiKSkNCg0KdGVfZDZfcm5hX2FkamFjZW5jeSA8LSBhZGphY2VuY3kod2djbmFfZGF0RXhwcltbInJuYSJdXSwNCnNlbGVjdENvbHMgPSBOVUxMLA0KdHlwZSA9ICJzaWduZWQiLA0KcG93ZXIgPSAxMiwgI3NldCBzb2Z0LXBvd2VyIHRvIDEyIGFzIGZvciBtb2R1bGUgY29tcHV0YXRpb24NCmNvckZuYyA9ICJjb3IiLCBjb3JPcHRpb25zID0gbGlzdCh1c2UgPSAicCIpLA0Kd2VpZ2h0cyA9IE5VTEwsDQpkaXN0Rm5jID0gImRpc3QiLCBkaXN0T3B0aW9ucyA9ICJtZXRob2QgPSAnd2FyZC5EMiciLA0Kd2VpZ2h0QXJnTmFtZXMgPSBjKCJ3ZWlnaHRzLngiLCAid2VpZ2h0cy55IikpDQoNCmBgYGANCg0KDQpgYGBge3Igc2VsZWN0ZWQgZ2VucyBhZGphYyBtYXRyaXh9DQoNCmVwaV9kNl9kbmFfYWRqYWNlbmN5X3NlbGVjdGVkPWVwaV9kNl9kbmFfYWRqYWNlbmN5W21vZHVsZV9wcm9tb3RlcnNfZXBpX2Q2LG1vZHVsZV9wcm9tb3RlcnNfZXBpX2Q2XQ0KZXBpX2Q2X3JuYV9hZGphY2VuY3lfc2VsZWN0ZWQ9ZXBpX2Q2X3JuYV9hZGphY2VuY3lbbW9kdWxlX3RyYW5zY3JpcHRzX2VwaV9kNixtb2R1bGVfdHJhbnNjcmlwdHNfZXBpX2Q2XQ0KDQpwZV9kOF9kbmFfYWRqYWNlbmN5X3NlbGVjdGVkPXBlX2Q4X2RuYV9hZGphY2VuY3lbbW9kdWxlX3Byb21vdGVyc19wZV9kOCxtb2R1bGVfcHJvbW90ZXJzX3BlX2Q4XQ0KcGVfZDhfcm5hX2FkamFjZW5jeV9zZWxlY3RlZD1wZV9kOF9ybmFfYWRqYWNlbmN5W21vZHVsZV90cmFuc2NyaXB0c19wZV9kOCxtb2R1bGVfdHJhbnNjcmlwdHNfcGVfZDhdDQoNCnRlX2Q2X2RuYV9hZGphY2VuY3lfc2VsZWN0ZWQ9dGVfZDZfZG5hX2FkamFjZW5jeVttb2R1bGVfcHJvbW90ZXJzX3RlX2Q2LG1vZHVsZV9wcm9tb3RlcnNfdGVfZDZdDQp0ZV9kNl9ybmFfYWRqYWNlbmN5X3NlbGVjdGVkPXRlX2Q2X3JuYV9hZGphY2VuY3lbbW9kdWxlX3RyYW5zY3JpcHRzX3RlX2Q2LG1vZHVsZV90cmFuc2NyaXB0c190ZV9kNl0NCg0KYGBgYA0KDQpgYGBge3IgYXR0ZW1wdCB0byBuZXR3b3JrfQ0KDQptb2R1bGVfcHJvbW90ZXJzX2VwaV9kNl9jb29yZD1OVUxMDQptb2R1bGVfcHJvbW90ZXJzX2VwaV9kNl9jb29yZCRzb3VyY2Vfbm9kZT1tb2R1bGVfcHJvbW90ZXJzX2VwaV9kNg0KbW9kdWxlX3Byb21vdGVyc19lcGlfZDZfY29vcmQkeF9zb3VyY2U9cmVwKDEsbGVuZ3RoKG1vZHVsZV9wcm9tb3RlcnNfZXBpX2Q2KSkNCm1vZHVsZV9wcm9tb3RlcnNfZXBpX2Q2X2Nvb3JkJHlfc291cmNlPXNlcSh0bz0oMTAwL2xlbmd0aChtb2R1bGVfcHJvbW90ZXJzX2VwaV9kNikpLCBmcm9tPTEwMCwgYnk9LSgxMDAvbGVuZ3RoKG1vZHVsZV9wcm9tb3RlcnNfZXBpX2Q2KSkpDQptb2R1bGVfcHJvbW90ZXJzX2VwaV9kNl9jb29yZD1hcy5kYXRhLmZyYW1lKG1vZHVsZV9wcm9tb3RlcnNfZXBpX2Q2X2Nvb3JkLGNvbC5uYW1lcz1uYW1lcyhtb2R1bGVfcHJvbW90ZXJzX2VwaV9kNl9jb29yZCkpDQoNCm1vZHVsZV90cmFuc2NyaXB0c19lcGlfZDZfY29vcmQ9TlVMTA0KbW9kdWxlX3RyYW5zY3JpcHRzX2VwaV9kNl9jb29yZCRzb3VyY2Vfbm9kZT1tb2R1bGVfdHJhbnNjcmlwdHNfZXBpX2Q2DQptb2R1bGVfdHJhbnNjcmlwdHNfZXBpX2Q2X2Nvb3JkJHhfc291cmNlPXJlcCgyLGxlbmd0aChtb2R1bGVfdHJhbnNjcmlwdHNfZXBpX2Q2KSkNCm1vZHVsZV90cmFuc2NyaXB0c19lcGlfZDZfY29vcmQkeV9zb3VyY2U9c2VxKHRvPSgxMDAvbGVuZ3RoKG1vZHVsZV90cmFuc2NyaXB0c19lcGlfZDYpKSwgZnJvbT0xMDAsIGJ5PS0oMTAwL2xlbmd0aChtb2R1bGVfdHJhbnNjcmlwdHNfZXBpX2Q2KSkpDQptb2R1bGVfdHJhbnNjcmlwdHNfZXBpX2Q2X2Nvb3JkPWFzLmRhdGEuZnJhbWUobW9kdWxlX3RyYW5zY3JpcHRzX2VwaV9kNl9jb29yZCxjb2wubmFtZXM9bmFtZXMobW9kdWxlX3RyYW5zY3JpcHRzX2VwaV9kNl9jb29yZCkpDQoNCmVwaV9tb2R1bGVzX2Nvb3JkPXJiaW5kKG1vZHVsZV9wcm9tb3RlcnNfZXBpX2Q2X2Nvb3JkLG1vZHVsZV90cmFuc2NyaXB0c19lcGlfZDZfY29vcmQpDQoNCg0KZXBpX2Q2X21vZHVsZV9hcnJvd3M9TlVMTA0KDQogIA0KZXBpX2Fycm93X2RuYV9kbmE9ZXBpX2Q2X2ludGVyYWN0aW9uX2RmW2VwaV9kNl9pbnRlcmFjdGlvbl9kZlssInNvdXJjZV9ub2RlIl0laW4lbW9kdWxlX3Byb21vdGVyc19lcGlfZDYgJiBlcGlfZDZfaW50ZXJhY3Rpb25fZGZbLCJ0YXJnZXRfbm9kZSJdJWluJW1vZHVsZV9wcm9tb3RlcnNfZXBpX2Q2LF0NCmVwaV9hcnJvd19kbmFfZG5hPWNiaW5kKGVwaV9hcnJvd19kbmFfZG5hLHhfYXJyb3c9cmVwKDEuMTMsbnJvdyhlcGlfYXJyb3dfZG5hX2RuYSkpLHhlbmRfYXJyb3c9cmVwKDEuMTMsbnJvdyhlcGlfYXJyb3dfZG5hX2RuYSkpKQ0KDQp5PU5VTEwNCg0KZm9yIChpIGluIGVwaV9hcnJvd19kbmFfZG5hWywic291cmNlX25vZGUiXSl7DQogIA0KICB5PWMoeSxtb2R1bGVfcHJvbW90ZXJzX2VwaV9kNl9jb29yZCR5X3NvdXJjZVttb2R1bGVfcHJvbW90ZXJzX2VwaV9kNl9jb29yZCRzb3VyY2Vfbm9kZT09aV0pICANCiAgDQp9DQoNCnllbmQ9TlVMTA0KDQpmb3IgKGkgaW4gZXBpX2Fycm93X2RuYV9kbmFbLCJ0YXJnZXRfbm9kZSJdKXsNCiAgDQogIHllbmQ9Yyh5ZW5kLG1vZHVsZV9wcm9tb3RlcnNfZXBpX2Q2X2Nvb3JkJHlfc291cmNlW21vZHVsZV9wcm9tb3RlcnNfZXBpX2Q2X2Nvb3JkJHNvdXJjZV9ub2RlPT1pXSkgIA0KICANCn0NCg0KZXBpX2Fycm93X2RuYV9kbmE9Y2JpbmQoZXBpX2Fycm93X2RuYV9kbmEseV9hcnJvdz15LHllbmRfYXJyb3c9eWVuZCkNCg0KZXBpX2Fycm93X2RuYV9ybmE9ZXBpX2Q2X2ludGVyYWN0aW9uX2RmW2VwaV9kNl9pbnRlcmFjdGlvbl9kZlssInNvdXJjZV9ub2RlIl0laW4lbW9kdWxlX3Byb21vdGVyc19lcGlfZDYgJiBlcGlfZDZfaW50ZXJhY3Rpb25fZGZbLCJ0YXJnZXRfbm9kZSJdJWluJW1vZHVsZV90cmFuc2NyaXB0c19lcGlfZDYsXQ0KZXBpX2Fycm93X2RuYV9ybmE9Y2JpbmQoZXBpX2Fycm93X2RuYV9ybmEseF9hcnJvdz1yZXAoMS4xMyxucm93KGVwaV9hcnJvd19kbmFfcm5hKSkseGVuZF9hcnJvdz1yZXAoMS45OCxucm93KGVwaV9hcnJvd19kbmFfcm5hKSkpDQoNCnk9TlVMTA0KDQpmb3IgKGkgaW4gZXBpX2Fycm93X2RuYV9ybmFbLCJzb3VyY2Vfbm9kZSJdKXsNCiAgDQogIHk9Yyh5LG1vZHVsZV9wcm9tb3RlcnNfZXBpX2Q2X2Nvb3JkJHlfc291cmNlW21vZHVsZV9wcm9tb3RlcnNfZXBpX2Q2X2Nvb3JkJHNvdXJjZV9ub2RlPT1pXSkgIA0KICANCn0NCg0KeWVuZD1OVUxMDQoNCmZvciAoaSBpbiBlcGlfYXJyb3dfZG5hX3JuYVssInRhcmdldF9ub2RlIl0pew0KICANCiAgeWVuZD1jKHllbmQsbW9kdWxlX3RyYW5zY3JpcHRzX2VwaV9kNl9jb29yZCR5X3NvdXJjZVttb2R1bGVfdHJhbnNjcmlwdHNfZXBpX2Q2X2Nvb3JkJHNvdXJjZV9ub2RlPT1pXSkgIA0KICANCn0NCg0KZXBpX2Fycm93X2RuYV9ybmE9Y2JpbmQoZXBpX2Fycm93X2RuYV9ybmEseV9hcnJvdz15LHllbmRfYXJyb3c9eWVuZCkNCg0KZXBpX2Fycm93X3JuYV9ybmE9ZXBpX2Q2X2ludGVyYWN0aW9uX2RmW2VwaV9kNl9pbnRlcmFjdGlvbl9kZlssInNvdXJjZV9ub2RlIl0laW4lbW9kdWxlX3RyYW5zY3JpcHRzX2VwaV9kNiAmIGVwaV9kNl9pbnRlcmFjdGlvbl9kZlssInRhcmdldF9ub2RlIl0laW4lbW9kdWxlX3RyYW5zY3JpcHRzX2VwaV9kNixdDQplcGlfYXJyb3dfcm5hX3JuYT1jYmluZChlcGlfYXJyb3dfcm5hX3JuYSx4X2Fycm93PXJlcCgxLjk4LG5yb3coZXBpX2Fycm93X3JuYV9ybmEpKSx4ZW5kX2Fycm93PXJlcCgxLjk4LG5yb3coZXBpX2Fycm93X3JuYV9ybmEpKSkNCg0KDQp5PU5VTEwNCg0KZm9yIChpIGluIGVwaV9hcnJvd19ybmFfcm5hWywic291cmNlX25vZGUiXSl7DQogIA0KICB5PWMoeSxtb2R1bGVfdHJhbnNjcmlwdHNfZXBpX2Q2X2Nvb3JkJHlfc291cmNlW21vZHVsZV90cmFuc2NyaXB0c19lcGlfZDZfY29vcmQkc291cmNlX25vZGU9PWldKSAgDQogIA0KfQ0KDQp5ZW5kPU5VTEwNCg0KZm9yIChpIGluIGVwaV9hcnJvd19ybmFfcm5hWywidGFyZ2V0X25vZGUiXSl7DQogIA0KICB5ZW5kPWMoeWVuZCxtb2R1bGVfdHJhbnNjcmlwdHNfZXBpX2Q2X2Nvb3JkJHlfc291cmNlW21vZHVsZV90cmFuc2NyaXB0c19lcGlfZDZfY29vcmQkc291cmNlX25vZGU9PWldKSAgDQogIA0KfQ0KDQplcGlfYXJyb3dfcm5hX3JuYT1jYmluZChlcGlfYXJyb3dfcm5hX3JuYSx5X2Fycm93PXkseWVuZF9hcnJvdz15ZW5kKQ0KDQoNCmVwaV9hcnJvd19ybmFfZG5hPWVwaV9kNl9pbnRlcmFjdGlvbl9kZltlcGlfZDZfaW50ZXJhY3Rpb25fZGZbLCJzb3VyY2Vfbm9kZSJdJWluJW1vZHVsZV90cmFuc2NyaXB0c19lcGlfZDYgJiBlcGlfZDZfaW50ZXJhY3Rpb25fZGZbLCJ0YXJnZXRfbm9kZSJdJWluJW1vZHVsZV9wcm9tb3RlcnNfZXBpX2Q2LF0NCmVwaV9hcnJvd19ybmFfZG5hPWNiaW5kKGVwaV9hcnJvd19ybmFfZG5hLHhfYXJyb3c9cmVwKDEuOTgsbnJvdyhlcGlfYXJyb3dfcm5hX2RuYSkpLHhlbmRfYXJyb3c9cmVwKDEuMTMsbnJvdyhlcGlfYXJyb3dfcm5hX2RuYSkpKQ0KDQoNCnk9TlVMTA0KDQpmb3IgKGkgaW4gZXBpX2Fycm93X3JuYV9kbmFbLCJzb3VyY2Vfbm9kZSJdKXsNCiAgDQogIHk9Yyh5LG1vZHVsZV90cmFuc2NyaXB0c19lcGlfZDZfY29vcmQkeV9zb3VyY2VbbW9kdWxlX3RyYW5zY3JpcHRzX2VwaV9kNl9jb29yZCRzb3VyY2Vfbm9kZT09aV0pICANCiAgDQp9DQoNCnllbmQ9TlVMTA0KDQpmb3IgKGkgaW4gZXBpX2Fycm93X3JuYV9kbmFbLCJ0YXJnZXRfbm9kZSJdKXsNCiAgDQogIHllbmQ9Yyh5ZW5kLG1vZHVsZV9wcm9tb3RlcnNfZXBpX2Q2X2Nvb3JkJHlfc291cmNlW21vZHVsZV9wcm9tb3RlcnNfZXBpX2Q2X2Nvb3JkJHNvdXJjZV9ub2RlPT1pXSkgIA0KICANCn0NCg0KZXBpX2Fycm93X3JuYV9kbmE9Y2JpbmQoZXBpX2Fycm93X3JuYV9kbmEseV9hcnJvdz15LHllbmRfYXJyb3c9eWVuZCkNCg0KDQplcGlfaW50ZXJhY3Rpb25fYXJyb3dzPXJiaW5kKGVwaV9hcnJvd19kbmFfZG5hLGVwaV9hcnJvd19kbmFfcm5hLGVwaV9hcnJvd19ybmFfcm5hLGVwaV9hcnJvd19ybmFfZG5hKQ0KDQplcGlfbW9kdWxlc19jb29yZCRtb2R1bGU9aWZlbHNlKGVwaV9tb2R1bGVzX2Nvb3JkJHhfc291cmNlPT0xLCJkbmEiLCJybmEiKQ0KZXBpX2ludGVyYWN0aW9uX2Fycm93cz1jYmluZChlcGlfaW50ZXJhY3Rpb25fYXJyb3dzLG1vZHVsZV9vcmlnaW49aWZlbHNlKGVwaV9pbnRlcmFjdGlvbl9hcnJvd3NbLCJ4X2Fycm93Il09PTEuMTMsImRuYSIsInJuYSIpKQ0KDQplcGlfbW9kdWxlc19pbnRlcmFjdGlvbl9wbG90PWdncGxvdChlcGlfbW9kdWxlc19jb29yZCxhZXMoeD14X3NvdXJjZSwgeT15X3NvdXJjZSwgbGFiZWw9c291cmNlX25vZGUpKSsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1zb3VyY2Vfbm9kZSwgY29sb3I9YXMuY2hhcmFjdGVyKG1vZHVsZSkpLGhqdXN0PTAsdmp1c3Q9MCkrDQogIGdlb21fcG9pbnQoYWVzKHggPSBpZmVsc2UoeF9zb3VyY2U9PTEsMS4xMywxLjk4KSx5PXlfc291cmNlLCBjb2xvcj1hcy5jaGFyYWN0ZXIobW9kdWxlKSkpKw0KICBnZW9tX2N1cnZlKGRhdGE9YXMuZGF0YS5mcmFtZShlcGlfaW50ZXJhY3Rpb25fYXJyb3dzKSxtYXBwaW5nPWFlcyh4PWFzLm51bWVyaWMoZXBpX2ludGVyYWN0aW9uX2Fycm93c1ssInhfYXJyb3ciXSkseT1hcy5udW1lcmljKGVwaV9pbnRlcmFjdGlvbl9hcnJvd3NbLCJ5X2Fycm93Il0pLHhlbmQ9YXMubnVtZXJpYyhlcGlfaW50ZXJhY3Rpb25fYXJyb3dzWywieGVuZF9hcnJvdyJdKSx5ZW5kPWFzLm51bWVyaWMoZXBpX2ludGVyYWN0aW9uX2Fycm93c1ssInllbmRfYXJyb3ciXSksY29sb3I9YXMuY2hhcmFjdGVyKGVwaV9pbnRlcmFjdGlvbl9hcnJvd3NbLCJtb2R1bGVfb3JpZ2luIl0pKSwgY3VydmF0dXJlID0gLTAuMiwgYXJyb3c9YXJyb3coKSwgc2l6ZT0xKSsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCIjMTNkMzcwIiwicHVycGxlIikpDQoNCg0KDQptb2R1bGVfcHJvbW90ZXJzX3BlX2Q4X2Nvb3JkPU5VTEwNCm1vZHVsZV9wcm9tb3RlcnNfcGVfZDhfY29vcmQkc291cmNlX25vZGU9bW9kdWxlX3Byb21vdGVyc19wZV9kOA0KbW9kdWxlX3Byb21vdGVyc19wZV9kOF9jb29yZCR4X3NvdXJjZT1yZXAoMSxsZW5ndGgobW9kdWxlX3Byb21vdGVyc19wZV9kOCkpDQptb2R1bGVfcHJvbW90ZXJzX3BlX2Q4X2Nvb3JkJHlfc291cmNlPXNlcSh0bz0oMTAwL2xlbmd0aChtb2R1bGVfcHJvbW90ZXJzX3BlX2Q4KSksIGZyb209MTAwLCBieT0tKDEwMC9sZW5ndGgobW9kdWxlX3Byb21vdGVyc19wZV9kOCkpKQ0KbW9kdWxlX3Byb21vdGVyc19wZV9kOF9jb29yZD1hcy5kYXRhLmZyYW1lKG1vZHVsZV9wcm9tb3RlcnNfcGVfZDhfY29vcmQsY29sLm5hbWVzPW5hbWVzKG1vZHVsZV9wcm9tb3RlcnNfcGVfZDhfY29vcmQpKQ0KDQptb2R1bGVfdHJhbnNjcmlwdHNfcGVfZDhfY29vcmQ9TlVMTA0KbW9kdWxlX3RyYW5zY3JpcHRzX3BlX2Q4X2Nvb3JkJHNvdXJjZV9ub2RlPW1vZHVsZV90cmFuc2NyaXB0c19wZV9kOA0KbW9kdWxlX3RyYW5zY3JpcHRzX3BlX2Q4X2Nvb3JkJHhfc291cmNlPXJlcCgyLGxlbmd0aChtb2R1bGVfdHJhbnNjcmlwdHNfcGVfZDgpKQ0KbW9kdWxlX3RyYW5zY3JpcHRzX3BlX2Q4X2Nvb3JkJHlfc291cmNlPXNlcSh0bz0oMTAwL2xlbmd0aChtb2R1bGVfdHJhbnNjcmlwdHNfcGVfZDgpKSwgZnJvbT0xMDAsIGJ5PS0oMTAwL2xlbmd0aChtb2R1bGVfdHJhbnNjcmlwdHNfcGVfZDgpKSkNCm1vZHVsZV90cmFuc2NyaXB0c19wZV9kOF9jb29yZD1hcy5kYXRhLmZyYW1lKG1vZHVsZV90cmFuc2NyaXB0c19wZV9kOF9jb29yZCxjb2wubmFtZXM9bmFtZXMobW9kdWxlX3RyYW5zY3JpcHRzX3BlX2Q4X2Nvb3JkKSkNCg0KcGVfbW9kdWxlc19jb29yZD1yYmluZChtb2R1bGVfcHJvbW90ZXJzX3BlX2Q4X2Nvb3JkLG1vZHVsZV90cmFuc2NyaXB0c19wZV9kOF9jb29yZCkNCg0KcGVfZDhfbW9kdWxlX2Fycm93cz1OVUxMDQoNCiAgDQpwZV9hcnJvd19kbmFfZG5hPXBlX2Q4X2ludGVyYWN0aW9uX2RmW3BlX2Q4X2ludGVyYWN0aW9uX2RmWywic291cmNlX25vZGUiXSVpbiVtb2R1bGVfcHJvbW90ZXJzX3BlX2Q4ICYgcGVfZDhfaW50ZXJhY3Rpb25fZGZbLCJ0YXJnZXRfbm9kZSJdJWluJW1vZHVsZV9wcm9tb3RlcnNfcGVfZDgsXQ0KcGVfYXJyb3dfZG5hX2RuYT1jYmluZChwZV9hcnJvd19kbmFfZG5hLHhfYXJyb3c9cmVwKDEuMTMsbnJvdyhwZV9hcnJvd19kbmFfZG5hKSkseGVuZF9hcnJvdz1yZXAoMS4xMyxucm93KHBlX2Fycm93X2RuYV9kbmEpKSkNCg0KeT1OVUxMDQoNCmZvciAoaSBpbiBwZV9hcnJvd19kbmFfZG5hWywic291cmNlX25vZGUiXSl7DQogIA0KICB5PWMoeSxtb2R1bGVfcHJvbW90ZXJzX3BlX2Q4X2Nvb3JkJHlfc291cmNlW21vZHVsZV9wcm9tb3RlcnNfcGVfZDhfY29vcmQkc291cmNlX25vZGU9PWldKSAgDQogIA0KfQ0KDQp5ZW5kPU5VTEwNCg0KZm9yIChpIGluIHBlX2Fycm93X2RuYV9kbmFbLCJ0YXJnZXRfbm9kZSJdKXsNCiAgDQogIHllbmQ9Yyh5ZW5kLG1vZHVsZV9wcm9tb3RlcnNfcGVfZDhfY29vcmQkeV9zb3VyY2VbbW9kdWxlX3Byb21vdGVyc19wZV9kOF9jb29yZCRzb3VyY2Vfbm9kZT09aV0pICANCiAgDQp9DQoNCnBlX2Fycm93X2RuYV9kbmE9Y2JpbmQocGVfYXJyb3dfZG5hX2RuYSx5X2Fycm93PXkseWVuZF9hcnJvdz15ZW5kKQ0KDQpwZV9hcnJvd19kbmFfcm5hPXBlX2Q4X2ludGVyYWN0aW9uX2RmW3BlX2Q4X2ludGVyYWN0aW9uX2RmWywic291cmNlX25vZGUiXSVpbiVtb2R1bGVfcHJvbW90ZXJzX3BlX2Q4ICYgcGVfZDhfaW50ZXJhY3Rpb25fZGZbLCJ0YXJnZXRfbm9kZSJdJWluJW1vZHVsZV90cmFuc2NyaXB0c19wZV9kOCxdDQpwZV9hcnJvd19kbmFfcm5hPWNiaW5kKHBlX2Fycm93X2RuYV9ybmEseF9hcnJvdz1yZXAoMS4xMyxucm93KHBlX2Fycm93X2RuYV9ybmEpKSx4ZW5kX2Fycm93PXJlcCgxLjk4LG5yb3cocGVfYXJyb3dfZG5hX3JuYSkpKQ0KDQp5PU5VTEwNCg0KZm9yIChpIGluIHBlX2Fycm93X2RuYV9ybmFbLCJzb3VyY2Vfbm9kZSJdKXsNCiAgDQogIHk9Yyh5LG1vZHVsZV9wcm9tb3RlcnNfcGVfZDhfY29vcmQkeV9zb3VyY2VbbW9kdWxlX3Byb21vdGVyc19wZV9kOF9jb29yZCRzb3VyY2Vfbm9kZT09aV0pICANCiAgDQp9DQoNCnllbmQ9TlVMTA0KDQpmb3IgKGkgaW4gcGVfYXJyb3dfZG5hX3JuYVssInRhcmdldF9ub2RlIl0pew0KICANCiAgeWVuZD1jKHllbmQsbW9kdWxlX3RyYW5zY3JpcHRzX3BlX2Q4X2Nvb3JkJHlfc291cmNlW21vZHVsZV90cmFuc2NyaXB0c19wZV9kOF9jb29yZCRzb3VyY2Vfbm9kZT09aV0pICANCiAgDQp9DQoNCnBlX2Fycm93X2RuYV9ybmE9Y2JpbmQocGVfYXJyb3dfZG5hX3JuYSx5X2Fycm93PXkseWVuZF9hcnJvdz15ZW5kKQ0KDQpwZV9hcnJvd19ybmFfcm5hPXBlX2Q4X2ludGVyYWN0aW9uX2RmW3BlX2Q4X2ludGVyYWN0aW9uX2RmWywic291cmNlX25vZGUiXSVpbiVtb2R1bGVfdHJhbnNjcmlwdHNfcGVfZDggJiBwZV9kOF9pbnRlcmFjdGlvbl9kZlssInRhcmdldF9ub2RlIl0laW4lbW9kdWxlX3RyYW5zY3JpcHRzX3BlX2Q4LF0NCnBlX2Fycm93X3JuYV9ybmE9Y2JpbmQocGVfYXJyb3dfcm5hX3JuYSx4X2Fycm93PXJlcCgxLjk4LG5yb3cocGVfYXJyb3dfcm5hX3JuYSkpLHhlbmRfYXJyb3c9cmVwKDEuOTgsbnJvdyhwZV9hcnJvd19ybmFfcm5hKSkpDQoNCg0KeT1OVUxMDQoNCmZvciAoaSBpbiBwZV9hcnJvd19ybmFfcm5hWywic291cmNlX25vZGUiXSl7DQogIA0KICB5PWMoeSxtb2R1bGVfdHJhbnNjcmlwdHNfcGVfZDhfY29vcmQkeV9zb3VyY2VbbW9kdWxlX3RyYW5zY3JpcHRzX3BlX2Q4X2Nvb3JkJHNvdXJjZV9ub2RlPT1pXSkgIA0KICANCn0NCg0KeWVuZD1OVUxMDQoNCmZvciAoaSBpbiBwZV9hcnJvd19ybmFfcm5hWywidGFyZ2V0X25vZGUiXSl7DQogIA0KICB5ZW5kPWMoeWVuZCxtb2R1bGVfdHJhbnNjcmlwdHNfcGVfZDhfY29vcmQkeV9zb3VyY2VbbW9kdWxlX3RyYW5zY3JpcHRzX3BlX2Q4X2Nvb3JkJHNvdXJjZV9ub2RlPT1pXSkgIA0KICANCn0NCg0KcGVfYXJyb3dfcm5hX3JuYT1jYmluZChwZV9hcnJvd19ybmFfcm5hLHlfYXJyb3c9eSx5ZW5kX2Fycm93PXllbmQpDQoNCg0KcGVfYXJyb3dfcm5hX2RuYT1wZV9kOF9pbnRlcmFjdGlvbl9kZltwZV9kOF9pbnRlcmFjdGlvbl9kZlssInNvdXJjZV9ub2RlIl0laW4lbW9kdWxlX3RyYW5zY3JpcHRzX3BlX2Q4ICYgcGVfZDhfaW50ZXJhY3Rpb25fZGZbLCJ0YXJnZXRfbm9kZSJdJWluJW1vZHVsZV9wcm9tb3RlcnNfcGVfZDgsXQ0KcGVfYXJyb3dfcm5hX2RuYT1jYmluZChwZV9hcnJvd19ybmFfZG5hLHhfYXJyb3c9cmVwKDEuOTgsbnJvdyhwZV9hcnJvd19ybmFfZG5hKSkseGVuZF9hcnJvdz1yZXAoMS4xMyxucm93KHBlX2Fycm93X3JuYV9kbmEpKSkNCg0KDQp5PU5VTEwNCg0KZm9yIChpIGluIHBlX2Fycm93X3JuYV9kbmFbLCJzb3VyY2Vfbm9kZSJdKXsNCiAgDQogIHk9Yyh5LG1vZHVsZV90cmFuc2NyaXB0c19wZV9kOF9jb29yZCR5X3NvdXJjZVttb2R1bGVfdHJhbnNjcmlwdHNfcGVfZDhfY29vcmQkc291cmNlX25vZGU9PWldKSAgDQogIA0KfQ0KDQp5ZW5kPU5VTEwNCg0KZm9yIChpIGluIHBlX2Fycm93X3JuYV9kbmFbLCJ0YXJnZXRfbm9kZSJdKXsNCiAgDQogIHllbmQ9Yyh5ZW5kLG1vZHVsZV9wcm9tb3RlcnNfcGVfZDhfY29vcmQkeV9zb3VyY2VbbW9kdWxlX3Byb21vdGVyc19wZV9kOF9jb29yZCRzb3VyY2Vfbm9kZT09aV0pICANCiAgDQp9DQoNCnBlX2Fycm93X3JuYV9kbmE9Y2JpbmQocGVfYXJyb3dfcm5hX2RuYSx5X2Fycm93PXkseWVuZF9hcnJvdz15ZW5kKQ0KDQoNCnBlX2ludGVyYWN0aW9uX2Fycm93cz1yYmluZChwZV9hcnJvd19kbmFfZG5hLHBlX2Fycm93X2RuYV9ybmEscGVfYXJyb3dfcm5hX3JuYSxwZV9hcnJvd19ybmFfZG5hKQ0KDQoNCnBlX21vZHVsZXNfY29vcmQkbW9kdWxlPWlmZWxzZShwZV9tb2R1bGVzX2Nvb3JkJHhfc291cmNlPT0xLCJkbmEiLCJybmEiKQ0KcGVfaW50ZXJhY3Rpb25fYXJyb3dzPWNiaW5kKHBlX2ludGVyYWN0aW9uX2Fycm93cyxtb2R1bGVfb3JpZ2luPWlmZWxzZShwZV9pbnRlcmFjdGlvbl9hcnJvd3NbLCJ4X2Fycm93Il09PTEuMTMsImRuYSIsInJuYSIpKQ0KDQpwZV9tb2R1bGVzX2ludGVyYWN0aW9uX3Bsb3Q9Z2dwbG90KHBlX21vZHVsZXNfY29vcmQsYWVzKHg9eF9zb3VyY2UsIHk9eV9zb3VyY2UsIGxhYmVsPXNvdXJjZV9ub2RlKSkrDQogIGdlb21fdGV4dChhZXMobGFiZWw9c291cmNlX25vZGUsIGNvbG9yPWFzLmNoYXJhY3Rlcihtb2R1bGUpKSxoanVzdD0wLHZqdXN0PTApKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gaWZlbHNlKHhfc291cmNlPT0xLDEuMTMsMS45OCkseT15X3NvdXJjZSwgY29sb3I9YXMuY2hhcmFjdGVyKG1vZHVsZSkpKSsNCiAgZ2VvbV9jdXJ2ZShkYXRhPWFzLmRhdGEuZnJhbWUocGVfaW50ZXJhY3Rpb25fYXJyb3dzKSxtYXBwaW5nPWFlcyh4PWFzLm51bWVyaWMocGVfaW50ZXJhY3Rpb25fYXJyb3dzWywieF9hcnJvdyJdKSx5PWFzLm51bWVyaWMocGVfaW50ZXJhY3Rpb25fYXJyb3dzWywieV9hcnJvdyJdKSx4ZW5kPWFzLm51bWVyaWMocGVfaW50ZXJhY3Rpb25fYXJyb3dzWywieGVuZF9hcnJvdyJdKSx5ZW5kPWFzLm51bWVyaWMocGVfaW50ZXJhY3Rpb25fYXJyb3dzWywieWVuZF9hcnJvdyJdKSxjb2xvcj1hcy5jaGFyYWN0ZXIocGVfaW50ZXJhY3Rpb25fYXJyb3dzWywibW9kdWxlX29yaWdpbiJdKSksIGN1cnZhdHVyZSA9IC0wLjIsIGFycm93PWFycm93KCksIHNpemU9MSkrDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiIzEzZDM3MCIsInB1cnBsZSIpKQ0KDQoNCg0KDQoNCm1vZHVsZV9wcm9tb3RlcnNfdGVfZDZfY29vcmQ9TlVMTA0KbW9kdWxlX3Byb21vdGVyc190ZV9kNl9jb29yZCRzb3VyY2Vfbm9kZT1tb2R1bGVfcHJvbW90ZXJzX3RlX2Q2DQptb2R1bGVfcHJvbW90ZXJzX3RlX2Q2X2Nvb3JkJHhfc291cmNlPXJlcCgxLGxlbmd0aChtb2R1bGVfcHJvbW90ZXJzX3RlX2Q2KSkNCm1vZHVsZV9wcm9tb3RlcnNfdGVfZDZfY29vcmQkeV9zb3VyY2U9c2VxKHRvPSgxMDAvbGVuZ3RoKG1vZHVsZV9wcm9tb3RlcnNfdGVfZDYpKSwgZnJvbT0xMDAsIGJ5PS0oMTAwL2xlbmd0aChtb2R1bGVfcHJvbW90ZXJzX3RlX2Q2KSkpDQptb2R1bGVfcHJvbW90ZXJzX3RlX2Q2X2Nvb3JkPWFzLmRhdGEuZnJhbWUobW9kdWxlX3Byb21vdGVyc190ZV9kNl9jb29yZCxjb2wubmFtZXM9bmFtZXMobW9kdWxlX3Byb21vdGVyc190ZV9kNl9jb29yZCkpDQoNCm1vZHVsZV90cmFuc2NyaXB0c190ZV9kNl9jb29yZD1OVUxMDQptb2R1bGVfdHJhbnNjcmlwdHNfdGVfZDZfY29vcmQkc291cmNlX25vZGU9bW9kdWxlX3RyYW5zY3JpcHRzX3RlX2Q2DQptb2R1bGVfdHJhbnNjcmlwdHNfdGVfZDZfY29vcmQkeF9zb3VyY2U9cmVwKDIsbGVuZ3RoKG1vZHVsZV90cmFuc2NyaXB0c190ZV9kNikpDQptb2R1bGVfdHJhbnNjcmlwdHNfdGVfZDZfY29vcmQkeV9zb3VyY2U9c2VxKHRvPSgxMDAvbGVuZ3RoKG1vZHVsZV90cmFuc2NyaXB0c190ZV9kNikpLCBmcm9tPTEwMCwgYnk9LSgxMDAvbGVuZ3RoKG1vZHVsZV90cmFuc2NyaXB0c190ZV9kNikpKQ0KbW9kdWxlX3RyYW5zY3JpcHRzX3RlX2Q2X2Nvb3JkPWFzLmRhdGEuZnJhbWUobW9kdWxlX3RyYW5zY3JpcHRzX3RlX2Q2X2Nvb3JkLGNvbC5uYW1lcz1uYW1lcyhtb2R1bGVfdHJhbnNjcmlwdHNfdGVfZDZfY29vcmQpKQ0KDQp0ZV9tb2R1bGVzX2Nvb3JkPXJiaW5kKG1vZHVsZV9wcm9tb3RlcnNfdGVfZDZfY29vcmQsbW9kdWxlX3RyYW5zY3JpcHRzX3RlX2Q2X2Nvb3JkKQ0KDQp0ZV9kNl9tb2R1bGVfYXJyb3dzPU5VTEwNCg0KICANCnRlX2Fycm93X2RuYV9kbmE9dGVfZDZfaW50ZXJhY3Rpb25fZGZbdGVfZDZfaW50ZXJhY3Rpb25fZGZbLCJzb3VyY2Vfbm9kZSJdJWluJW1vZHVsZV9wcm9tb3RlcnNfdGVfZDYgJiB0ZV9kNl9pbnRlcmFjdGlvbl9kZlssInRhcmdldF9ub2RlIl0laW4lbW9kdWxlX3Byb21vdGVyc190ZV9kNixdDQp0ZV9hcnJvd19kbmFfZG5hPWNiaW5kKHRlX2Fycm93X2RuYV9kbmEseF9hcnJvdz1yZXAoMS4xMyxucm93KHRlX2Fycm93X2RuYV9kbmEpKSx4ZW5kX2Fycm93PXJlcCgxLjEzLG5yb3codGVfYXJyb3dfZG5hX2RuYSkpKQ0KDQp5PU5VTEwNCg0KZm9yIChpIGluIHRlX2Fycm93X2RuYV9kbmFbLCJzb3VyY2Vfbm9kZSJdKXsNCiAgDQogIHk9Yyh5LG1vZHVsZV9wcm9tb3RlcnNfdGVfZDZfY29vcmQkeV9zb3VyY2VbbW9kdWxlX3Byb21vdGVyc190ZV9kNl9jb29yZCRzb3VyY2Vfbm9kZT09aV0pICANCiAgDQp9DQoNCnllbmQ9TlVMTA0KDQpmb3IgKGkgaW4gdGVfYXJyb3dfZG5hX2RuYVssInRhcmdldF9ub2RlIl0pew0KICANCiAgeWVuZD1jKHllbmQsbW9kdWxlX3Byb21vdGVyc190ZV9kNl9jb29yZCR5X3NvdXJjZVttb2R1bGVfcHJvbW90ZXJzX3RlX2Q2X2Nvb3JkJHNvdXJjZV9ub2RlPT1pXSkgIA0KICANCn0NCg0KdGVfYXJyb3dfZG5hX2RuYT1jYmluZCh0ZV9hcnJvd19kbmFfZG5hLHlfYXJyb3c9eSx5ZW5kX2Fycm93PXllbmQpDQoNCnRlX2Fycm93X2RuYV9ybmE9dGVfZDZfaW50ZXJhY3Rpb25fZGZbdGVfZDZfaW50ZXJhY3Rpb25fZGZbLCJzb3VyY2Vfbm9kZSJdJWluJW1vZHVsZV9wcm9tb3RlcnNfdGVfZDYgJiB0ZV9kNl9pbnRlcmFjdGlvbl9kZlssInRhcmdldF9ub2RlIl0laW4lbW9kdWxlX3RyYW5zY3JpcHRzX3RlX2Q2LF0NCnRlX2Fycm93X2RuYV9ybmE9Y2JpbmQodGVfYXJyb3dfZG5hX3JuYSx4X2Fycm93PXJlcCgxLjEzLG5yb3codGVfYXJyb3dfZG5hX3JuYSkpLHhlbmRfYXJyb3c9cmVwKDEuOTgsbnJvdyh0ZV9hcnJvd19kbmFfcm5hKSkpDQoNCnk9TlVMTA0KDQpmb3IgKGkgaW4gdGVfYXJyb3dfZG5hX3JuYVssInNvdXJjZV9ub2RlIl0pew0KICANCiAgeT1jKHksbW9kdWxlX3Byb21vdGVyc190ZV9kNl9jb29yZCR5X3NvdXJjZVttb2R1bGVfcHJvbW90ZXJzX3RlX2Q2X2Nvb3JkJHNvdXJjZV9ub2RlPT1pXSkgIA0KICANCn0NCg0KeWVuZD1OVUxMDQoNCmZvciAoaSBpbiB0ZV9hcnJvd19kbmFfcm5hWywidGFyZ2V0X25vZGUiXSl7DQogIA0KICB5ZW5kPWMoeWVuZCxtb2R1bGVfdHJhbnNjcmlwdHNfdGVfZDZfY29vcmQkeV9zb3VyY2VbbW9kdWxlX3RyYW5zY3JpcHRzX3RlX2Q2X2Nvb3JkJHNvdXJjZV9ub2RlPT1pXSkgIA0KICANCn0NCg0KdGVfYXJyb3dfZG5hX3JuYT1jYmluZCh0ZV9hcnJvd19kbmFfcm5hLHlfYXJyb3c9eSx5ZW5kX2Fycm93PXllbmQpDQoNCnRlX2Fycm93X3JuYV9ybmE9dGVfZDZfaW50ZXJhY3Rpb25fZGZbdGVfZDZfaW50ZXJhY3Rpb25fZGZbLCJzb3VyY2Vfbm9kZSJdJWluJW1vZHVsZV90cmFuc2NyaXB0c190ZV9kNiAmIHRlX2Q2X2ludGVyYWN0aW9uX2RmWywidGFyZ2V0X25vZGUiXSVpbiVtb2R1bGVfdHJhbnNjcmlwdHNfdGVfZDYsXQ0KdGVfYXJyb3dfcm5hX3JuYT1jYmluZCh0ZV9hcnJvd19ybmFfcm5hLHhfYXJyb3c9cmVwKDEuOTgsbnJvdyh0ZV9hcnJvd19ybmFfcm5hKSkseGVuZF9hcnJvdz1yZXAoMS45OCxucm93KHRlX2Fycm93X3JuYV9ybmEpKSkNCg0KDQp5PU5VTEwNCg0KZm9yIChpIGluIHRlX2Fycm93X3JuYV9ybmFbLCJzb3VyY2Vfbm9kZSJdKXsNCiAgDQogIHk9Yyh5LG1vZHVsZV90cmFuc2NyaXB0c190ZV9kNl9jb29yZCR5X3NvdXJjZVttb2R1bGVfdHJhbnNjcmlwdHNfdGVfZDZfY29vcmQkc291cmNlX25vZGU9PWldKSAgDQogIA0KfQ0KDQp5ZW5kPU5VTEwNCg0KZm9yIChpIGluIHRlX2Fycm93X3JuYV9ybmFbLCJ0YXJnZXRfbm9kZSJdKXsNCiAgDQogIHllbmQ9Yyh5ZW5kLG1vZHVsZV90cmFuc2NyaXB0c190ZV9kNl9jb29yZCR5X3NvdXJjZVttb2R1bGVfdHJhbnNjcmlwdHNfdGVfZDZfY29vcmQkc291cmNlX25vZGU9PWldKSAgDQogIA0KfQ0KDQp0ZV9hcnJvd19ybmFfcm5hPWNiaW5kKHRlX2Fycm93X3JuYV9ybmEseV9hcnJvdz15LHllbmRfYXJyb3c9eWVuZCkNCg0KDQp0ZV9hcnJvd19ybmFfZG5hPXRlX2Q2X2ludGVyYWN0aW9uX2RmW3RlX2Q2X2ludGVyYWN0aW9uX2RmWywic291cmNlX25vZGUiXSVpbiVtb2R1bGVfdHJhbnNjcmlwdHNfdGVfZDYgJiB0ZV9kNl9pbnRlcmFjdGlvbl9kZlssInRhcmdldF9ub2RlIl0laW4lbW9kdWxlX3Byb21vdGVyc190ZV9kNixdDQp0ZV9hcnJvd19ybmFfZG5hPWNiaW5kKHRlX2Fycm93X3JuYV9kbmEseF9hcnJvdz1yZXAoMS45OCxucm93KHRlX2Fycm93X3JuYV9kbmEpKSx4ZW5kX2Fycm93PXJlcCgxLjEzLG5yb3codGVfYXJyb3dfcm5hX2RuYSkpKQ0KDQoNCnk9TlVMTA0KDQpmb3IgKGkgaW4gdGVfYXJyb3dfcm5hX2RuYVssInNvdXJjZV9ub2RlIl0pew0KICANCiAgeT1jKHksbW9kdWxlX3RyYW5zY3JpcHRzX3RlX2Q2X2Nvb3JkJHlfc291cmNlW21vZHVsZV90cmFuc2NyaXB0c190ZV9kNl9jb29yZCRzb3VyY2Vfbm9kZT09aV0pICANCiAgDQp9DQoNCnllbmQ9TlVMTA0KDQpmb3IgKGkgaW4gdGVfYXJyb3dfcm5hX2RuYVssInRhcmdldF9ub2RlIl0pew0KICANCiAgeWVuZD1jKHllbmQsbW9kdWxlX3Byb21vdGVyc190ZV9kNl9jb29yZCR5X3NvdXJjZVttb2R1bGVfcHJvbW90ZXJzX3RlX2Q2X2Nvb3JkJHNvdXJjZV9ub2RlPT1pXSkgIA0KICANCn0NCg0KdGVfYXJyb3dfcm5hX2RuYT1jYmluZCh0ZV9hcnJvd19ybmFfZG5hLHlfYXJyb3c9eSx5ZW5kX2Fycm93PXllbmQpDQoNCg0KdGVfaW50ZXJhY3Rpb25fYXJyb3dzPXJiaW5kKHRlX2Fycm93X2RuYV9kbmEsdGVfYXJyb3dfZG5hX3JuYSx0ZV9hcnJvd19ybmFfcm5hLHRlX2Fycm93X3JuYV9kbmEpDQoNCg0KdGVfbW9kdWxlc19jb29yZCRtb2R1bGU9aWZlbHNlKHRlX21vZHVsZXNfY29vcmQkeF9zb3VyY2U9PTEsImRuYSIsInJuYSIpDQp0ZV9pbnRlcmFjdGlvbl9hcnJvd3M9Y2JpbmQodGVfaW50ZXJhY3Rpb25fYXJyb3dzLG1vZHVsZV9vcmlnaW49aWZlbHNlKHRlX2ludGVyYWN0aW9uX2Fycm93c1ssInhfYXJyb3ciXT09MS4xMywiZG5hIiwicm5hIikpDQoNCnRlX21vZHVsZXNfaW50ZXJhY3Rpb25fcGxvdD1nZ3Bsb3QodGVfbW9kdWxlc19jb29yZCxhZXMoeD14X3NvdXJjZSwgeT15X3NvdXJjZSwgbGFiZWw9c291cmNlX25vZGUpKSsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1zb3VyY2Vfbm9kZSwgY29sb3I9YXMuY2hhcmFjdGVyKG1vZHVsZSkpLGhqdXN0PTAsdmp1c3Q9MCkrDQogIGdlb21fcG9pbnQoYWVzKHggPSBpZmVsc2UoeF9zb3VyY2U9PTEsMS4xMywxLjk4KSx5PXlfc291cmNlLCBjb2xvcj1hcy5jaGFyYWN0ZXIobW9kdWxlKSkpKw0KICBnZW9tX2N1cnZlKGRhdGE9YXMuZGF0YS5mcmFtZSh0ZV9pbnRlcmFjdGlvbl9hcnJvd3MpLG1hcHBpbmc9YWVzKHg9YXMubnVtZXJpYyh0ZV9pbnRlcmFjdGlvbl9hcnJvd3NbLCJ4X2Fycm93Il0pLHk9YXMubnVtZXJpYyh0ZV9pbnRlcmFjdGlvbl9hcnJvd3NbLCJ5X2Fycm93Il0pLHhlbmQ9YXMubnVtZXJpYyh0ZV9pbnRlcmFjdGlvbl9hcnJvd3NbLCJ4ZW5kX2Fycm93Il0pLHllbmQ9YXMubnVtZXJpYyh0ZV9pbnRlcmFjdGlvbl9hcnJvd3NbLCJ5ZW5kX2Fycm93Il0pLGNvbG9yPWFzLmNoYXJhY3Rlcih0ZV9pbnRlcmFjdGlvbl9hcnJvd3NbLCJtb2R1bGVfb3JpZ2luIl0pKSwgY3VydmF0dXJlID0gLTAuMiwgYXJyb3c9YXJyb3coKSwgc2l6ZT0xKSsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCIjMTNkMzcwIiwicHVycGxlIikpDQoNCg0KDQoNCmVwaV9kNl9kbmFfc2l6ZSA9IE5VTEwNCg0KZm9yIChpIGluIG1vZHVsZV9wcm9tb3RlcnNfZXBpX2Q2KXsNCiAgZXBpX2Q2X2RuYV9zaXplPWMoZXBpX2Q2X2RuYV9zaXplLHN1bShlcGlfaW50ZXJhY3Rpb25fYXJyb3dzWywic291cmNlX25vZGUiXT09aSkpDQp9DQpuYW1lcyhlcGlfZDZfZG5hX3NpemUpPW1vZHVsZV9wcm9tb3RlcnNfZXBpX2Q2DQoNCg0KDQplcGlfZDZfcm5hX3NpemUgPSBOVUxMDQoNCmZvciAoaSBpbiBtb2R1bGVfdHJhbnNjcmlwdHNfZXBpX2Q2KXsNCiAgDQogIGVwaV9kNl9ybmFfc2l6ZT1jKGVwaV9kNl9ybmFfc2l6ZSxzdW0oZXBpX2ludGVyYWN0aW9uX2Fycm93c1ssInNvdXJjZV9ub2RlIl09PWkpKQ0KfQ0KbmFtZXMoZXBpX2Q2X3JuYV9zaXplKT1tb2R1bGVfdHJhbnNjcmlwdHNfZXBpX2Q2DQoNCg0KcGVfZDhfZG5hX3NpemUgPSBOVUxMDQoNCmZvciAoaSBpbiBtb2R1bGVfcHJvbW90ZXJzX3BlX2Q4KXsNCiAgcGVfZDhfZG5hX3NpemU9YyhwZV9kOF9kbmFfc2l6ZSxzdW0ocGVfaW50ZXJhY3Rpb25fYXJyb3dzWywic291cmNlX25vZGUiXT09aSkpDQp9DQpuYW1lcyhwZV9kOF9kbmFfc2l6ZSk9bW9kdWxlX3Byb21vdGVyc19wZV9kOA0KDQoNCnBlX2Q4X3JuYV9zaXplID0gTlVMTA0KDQpmb3IgKGkgaW4gbW9kdWxlX3RyYW5zY3JpcHRzX3BlX2Q4KXsNCiAgcGVfZDhfcm5hX3NpemU9YyhwZV9kOF9ybmFfc2l6ZSxzdW0ocGVfaW50ZXJhY3Rpb25fYXJyb3dzWywic291cmNlX25vZGUiXT09aSkpDQp9DQpuYW1lcyhwZV9kOF9ybmFfc2l6ZSk9bW9kdWxlX3RyYW5zY3JpcHRzX3BlX2Q4DQoNCg0KdGVfZDZfZG5hX3NpemUgPSBOVUxMDQoNCmZvciAoaSBpbiBtb2R1bGVfcHJvbW90ZXJzX3RlX2Q2KXsNCiAgdGVfZDZfZG5hX3NpemU9Yyh0ZV9kNl9kbmFfc2l6ZSxzdW0odGVfaW50ZXJhY3Rpb25fYXJyb3dzWywic291cmNlX25vZGUiXT09aSkpDQp9DQpuYW1lcyh0ZV9kNl9kbmFfc2l6ZSk9bW9kdWxlX3Byb21vdGVyc190ZV9kNg0KDQoNCnRlX2Q2X3JuYV9zaXplID0gTlVMTA0KDQpmb3IgKGkgaW4gbW9kdWxlX3RyYW5zY3JpcHRzX3RlX2Q2KXsNCiAgdGVfZDZfcm5hX3NpemU9Yyh0ZV9kNl9ybmFfc2l6ZSxzdW0odGVfaW50ZXJhY3Rpb25fYXJyb3dzWywic291cmNlX25vZGUiXT09aSkpDQp9DQpuYW1lcyh0ZV9kNl9ybmFfc2l6ZSk9bW9kdWxlX3RyYW5zY3JpcHRzX3RlX2Q2DQoNCiMgc2F2ZS5pbWFnZSgiMjAyMV8wNl8xNF9uZXR3b3JrLlJEYXRhIikNCmxvYWQoIjIwMjFfMDZfMTRfbmV0d29yay5SRGF0YSIpDQpgYGBgDQoNCg0KYGBgYHtyIGFwcGx5IFBDQSB0byBiaW5hcml6ZWQgbW9kdWxlIGFkamFjZW5jeSBtYXRyaWNlc30NCg0KZXBpX2Q2X2RuYV9tb2R1bGVfYWNwPUFDUChpZmVsc2UoZXBpX2Q2X2RuYV9hZGphY2VuY3lfc2VsZWN0ZWQ+bWVkaWFuKGVwaV9kNl9kbmFfYWRqYWNlbmN5X3NlbGVjdGVkKSwxLDApKQ0KZXBpX2Q2X3JuYV9tb2R1bGVfYWNwPUFDUChpZmVsc2UoZXBpX2Q2X3JuYV9hZGphY2VuY3lfc2VsZWN0ZWQ+bWVkaWFuKGVwaV9kNl9ybmFfYWRqYWNlbmN5X3NlbGVjdGVkKSwxLDApKQ0KDQpwZV9kOF9kbmFfbW9kdWxlX2FjcD1BQ1AoaWZlbHNlKHBlX2Q4X2RuYV9hZGphY2VuY3lfc2VsZWN0ZWQ+bWVkaWFuKHBlX2Q4X2RuYV9hZGphY2VuY3lfc2VsZWN0ZWQpLDEsMCkpDQpwZV9kOF9ybmFfbW9kdWxlX2FjcD1BQ1AoaWZlbHNlKHBlX2Q4X3JuYV9hZGphY2VuY3lfc2VsZWN0ZWQ+bWVkaWFuKHBlX2Q4X3JuYV9hZGphY2VuY3lfc2VsZWN0ZWQpLDEsMCkpDQoNCnRlX2Q2X2RuYV9tb2R1bGVfYWNwPUFDUChpZmVsc2UodGVfZDZfZG5hX2FkamFjZW5jeV9zZWxlY3RlZD5tZWRpYW4odGVfZDZfZG5hX2FkamFjZW5jeV9zZWxlY3RlZCksMSwwKSkNCnRlX2Q2X3JuYV9tb2R1bGVfYWNwPUFDUChpZmVsc2UodGVfZDZfcm5hX2FkamFjZW5jeV9zZWxlY3RlZD5tZWRpYW4odGVfZDZfcm5hX2FkamFjZW5jeV9zZWxlY3RlZCksMSwwKSkNCg0KYGBgYA0KDQoNCmBgYGB7ciBidWlsZGluZyAzRCBhcnJvd3N9DQoNCmVwaV9kNl9tb2R1bGVfM2RfYXJyb3dzPU5VTEwNCg0KICANCmVwaV8zZF9hcnJvd19kbmFfZG5hPWVwaV9kNl9pbnRlcmFjdGlvbl9kZltlcGlfZDZfaW50ZXJhY3Rpb25fZGZbLCJzb3VyY2Vfbm9kZSJdJWluJW1vZHVsZV9wcm9tb3RlcnNfZXBpX2Q2ICYgZXBpX2Q2X2ludGVyYWN0aW9uX2RmWywidGFyZ2V0X25vZGUiXSVpbiVtb2R1bGVfcHJvbW90ZXJzX2VwaV9kNixjKCJzb3VyY2Vfbm9kZSIsInRhcmdldF9ub2RlIildDQoNCnh5ej1OVUxMDQpmb3IgKGkgaW4gZXBpXzNkX2Fycm93X2RuYV9kbmFbLCJzb3VyY2Vfbm9kZSJdKXsNCiAgeHl6PXJiaW5kKHh5eixlcGlfZDZfZG5hX21vZHVsZV9hY3AkeFtyb3duYW1lcyhlcGlfZDZfZG5hX21vZHVsZV9hY3AkeCk9PWksYygxOjMpXSkgIA0KfQ0KDQoNCg0KeHl6X2VuZD1OVUxMDQpmb3IgKGkgaW4gZXBpXzNkX2Fycm93X2RuYV9kbmFbLCJ0YXJnZXRfbm9kZSJdKXsNCiAgeHl6X2VuZD1yYmluZCh4eXpfZW5kLGVwaV9kNl9kbmFfbW9kdWxlX2FjcCR4W3Jvd25hbWVzKGVwaV9kNl9kbmFfbW9kdWxlX2FjcCR4KT09aSxjKDE6MyldKSAgDQp9DQoNCmVwaV9kbmFfZG5hXzNkX2Fycm93cz1kYXRhLmZyYW1lKGVwaV8zZF9hcnJvd19kbmFfZG5hLHhfM2RfYXJyb3c9eHl6WywxXSx5XzNkX2Fycm93PXh5elssMl0sIHpfM2RfYXJyb3c9eHl6WywzXSx4ZW5kXzNkX2Fycm93PXh5el9lbmRbLDFdLHllbmRfM2RfYXJyb3c9eHl6X2VuZFssMl0semVuZF8zZF9hcnJvdz14eXpfZW5kWywzXSkNCg0KDQoNCg0KDQpwZV9kOF9tb2R1bGVfM2RfYXJyb3dzPU5VTEwNCg0KICANCnBlXzNkX2Fycm93X2RuYV9kbmE9cGVfZDhfaW50ZXJhY3Rpb25fZGZbcGVfZDhfaW50ZXJhY3Rpb25fZGZbLCJzb3VyY2Vfbm9kZSJdJWluJW1vZHVsZV9wcm9tb3RlcnNfcGVfZDggJiBwZV9kOF9pbnRlcmFjdGlvbl9kZlssInRhcmdldF9ub2RlIl0laW4lbW9kdWxlX3Byb21vdGVyc19wZV9kOCxjKCJzb3VyY2Vfbm9kZSIsInRhcmdldF9ub2RlIildDQoNCnh5ej1OVUxMDQpmb3IgKGkgaW4gcGVfM2RfYXJyb3dfZG5hX2RuYVssInNvdXJjZV9ub2RlIl0pew0KICB4eXo9cmJpbmQoeHl6LHBlX2Q4X2RuYV9tb2R1bGVfYWNwJHhbcm93bmFtZXMocGVfZDhfZG5hX21vZHVsZV9hY3AkeCk9PWksYygxOjMpXSkgIA0KfQ0KDQoNCg0KeHl6X2VuZD1OVUxMDQpmb3IgKGkgaW4gcGVfM2RfYXJyb3dfZG5hX2RuYVssInRhcmdldF9ub2RlIl0pew0KICB4eXpfZW5kPXJiaW5kKHh5el9lbmQscGVfZDhfZG5hX21vZHVsZV9hY3AkeFtyb3duYW1lcyhwZV9kOF9kbmFfbW9kdWxlX2FjcCR4KT09aSxjKDE6MyldKSAgDQp9DQoNCnBlX2RuYV9kbmFfM2RfYXJyb3dzPWRhdGEuZnJhbWUocGVfM2RfYXJyb3dfZG5hX2RuYSx4XzNkX2Fycm93PXh5elssMV0seV8zZF9hcnJvdz14eXpbLDJdLCB6XzNkX2Fycm93PXh5elssM10seGVuZF8zZF9hcnJvdz14eXpfZW5kWywxXSx5ZW5kXzNkX2Fycm93PXh5el9lbmRbLDJdLHplbmRfM2RfYXJyb3c9eHl6X2VuZFssM10pDQoNCg0KDQoNCg0KdGVfZDZfbW9kdWxlXzNkX2Fycm93cz1OVUxMDQoNCiAgDQp0ZV8zZF9hcnJvd19kbmFfZG5hPXRlX2Q2X2ludGVyYWN0aW9uX2RmW3RlX2Q2X2ludGVyYWN0aW9uX2RmWywic291cmNlX25vZGUiXSVpbiVtb2R1bGVfcHJvbW90ZXJzX3RlX2Q2ICYgdGVfZDZfaW50ZXJhY3Rpb25fZGZbLCJ0YXJnZXRfbm9kZSJdJWluJW1vZHVsZV9wcm9tb3RlcnNfdGVfZDYsYygic291cmNlX25vZGUiLCJ0YXJnZXRfbm9kZSIpXQ0KDQp4eXo9TlVMTA0KZm9yIChpIGluIHRlXzNkX2Fycm93X2RuYV9kbmFbLCJzb3VyY2Vfbm9kZSJdKXsNCiAgeHl6PXJiaW5kKHh5eix0ZV9kNl9kbmFfbW9kdWxlX2FjcCR4W3Jvd25hbWVzKHRlX2Q2X2RuYV9tb2R1bGVfYWNwJHgpPT1pLGMoMTozKV0pICANCn0NCg0KDQoNCnh5el9lbmQ9TlVMTA0KZm9yIChpIGluIHRlXzNkX2Fycm93X2RuYV9kbmFbLCJ0YXJnZXRfbm9kZSJdKXsNCiAgeHl6X2VuZD1yYmluZCh4eXpfZW5kLHRlX2Q2X2RuYV9tb2R1bGVfYWNwJHhbcm93bmFtZXModGVfZDZfZG5hX21vZHVsZV9hY3AkeCk9PWksYygxOjMpXSkgIA0KfQ0KDQp0ZV9kbmFfZG5hXzNkX2Fycm93cz1kYXRhLmZyYW1lKHRlXzNkX2Fycm93X2RuYV9kbmEseF8zZF9hcnJvdz14eXpbLDFdLHlfM2RfYXJyb3c9eHl6WywyXSwgel8zZF9hcnJvdz14eXpbLDNdLHhlbmRfM2RfYXJyb3c9eHl6X2VuZFssMV0seWVuZF8zZF9hcnJvdz14eXpfZW5kWywyXSx6ZW5kXzNkX2Fycm93PXh5el9lbmRbLDNdKQ0KDQoNCg0KDQoNCg0KZXBpX2Q2X21vZHVsZV8zZF9hcnJvd3M9TlVMTA0KDQogIA0KZXBpXzNkX2Fycm93X3JuYV9ybmE9ZXBpX2Q2X2ludGVyYWN0aW9uX2RmW2VwaV9kNl9pbnRlcmFjdGlvbl9kZlssInNvdXJjZV9ub2RlIl0laW4lbW9kdWxlX3RyYW5zY3JpcHRzX2VwaV9kNiAmIGVwaV9kNl9pbnRlcmFjdGlvbl9kZlssInRhcmdldF9ub2RlIl0laW4lbW9kdWxlX3RyYW5zY3JpcHRzX2VwaV9kNixjKCJzb3VyY2Vfbm9kZSIsInRhcmdldF9ub2RlIildDQoNCnh5ej1OVUxMDQpmb3IgKGkgaW4gZXBpXzNkX2Fycm93X3JuYV9ybmFbLCJzb3VyY2Vfbm9kZSJdKXsNCiAgeHl6PXJiaW5kKHh5eixlcGlfZDZfcm5hX21vZHVsZV9hY3AkeFtyb3duYW1lcyhlcGlfZDZfcm5hX21vZHVsZV9hY3AkeCk9PWksYygxOjMpXSs1KSAgDQp9DQoNCg0KDQp4eXpfZW5kPU5VTEwNCmZvciAoaSBpbiBlcGlfM2RfYXJyb3dfcm5hX3JuYVssInRhcmdldF9ub2RlIl0pew0KICB4eXpfZW5kPXJiaW5kKHh5el9lbmQsZXBpX2Q2X3JuYV9tb2R1bGVfYWNwJHhbcm93bmFtZXMoZXBpX2Q2X3JuYV9tb2R1bGVfYWNwJHgpPT1pLGMoMTozKV0rNSkgIA0KfQ0KDQplcGlfcm5hX3JuYV8zZF9hcnJvd3M9ZGF0YS5mcmFtZShlcGlfM2RfYXJyb3dfcm5hX3JuYSx4XzNkX2Fycm93PXh5elssMV0seV8zZF9hcnJvdz14eXpbLDJdLCB6XzNkX2Fycm93PXh5elssM10seGVuZF8zZF9hcnJvdz14eXpfZW5kWywxXSx5ZW5kXzNkX2Fycm93PXh5el9lbmRbLDJdLHplbmRfM2RfYXJyb3c9eHl6X2VuZFssM10pDQoNCg0KDQoNCg0KcGVfZDhfbW9kdWxlXzNkX2Fycm93cz1OVUxMDQoNCiAgDQpwZV8zZF9hcnJvd19ybmFfcm5hPXBlX2Q4X2ludGVyYWN0aW9uX2RmW3BlX2Q4X2ludGVyYWN0aW9uX2RmWywic291cmNlX25vZGUiXSVpbiVtb2R1bGVfdHJhbnNjcmlwdHNfcGVfZDggJiBwZV9kOF9pbnRlcmFjdGlvbl9kZlssInRhcmdldF9ub2RlIl0laW4lbW9kdWxlX3RyYW5zY3JpcHRzX3BlX2Q4LGMoInNvdXJjZV9ub2RlIiwidGFyZ2V0X25vZGUiKV0NCg0KeHl6PU5VTEwNCmZvciAoaSBpbiBwZV8zZF9hcnJvd19ybmFfcm5hWywic291cmNlX25vZGUiXSl7DQogIHh5ej1yYmluZCh4eXoscGVfZDhfcm5hX21vZHVsZV9hY3AkeFtyb3duYW1lcyhwZV9kOF9ybmFfbW9kdWxlX2FjcCR4KT09aSxjKDE6MyldKzUpICANCn0NCg0KDQoNCnh5el9lbmQ9TlVMTA0KZm9yIChpIGluIHBlXzNkX2Fycm93X3JuYV9ybmFbLCJ0YXJnZXRfbm9kZSJdKXsNCiAgeHl6X2VuZD1yYmluZCh4eXpfZW5kLHBlX2Q4X3JuYV9tb2R1bGVfYWNwJHhbcm93bmFtZXMocGVfZDhfcm5hX21vZHVsZV9hY3AkeCk9PWksYygxOjMpXSs1KSAgDQp9DQoNCnBlX3JuYV9ybmFfM2RfYXJyb3dzPWRhdGEuZnJhbWUocGVfM2RfYXJyb3dfcm5hX3JuYSx4XzNkX2Fycm93PXh5elssMV0seV8zZF9hcnJvdz14eXpbLDJdLCB6XzNkX2Fycm93PXh5elssM10seGVuZF8zZF9hcnJvdz14eXpfZW5kWywxXSx5ZW5kXzNkX2Fycm93PXh5el9lbmRbLDJdLHplbmRfM2RfYXJyb3c9eHl6X2VuZFssM10pDQoNCg0KDQoNCg0KdGVfZDZfbW9kdWxlXzNkX2Fycm93cz1OVUxMDQoNCiAgDQp0ZV8zZF9hcnJvd19ybmFfcm5hPXRlX2Q2X2ludGVyYWN0aW9uX2RmW3RlX2Q2X2ludGVyYWN0aW9uX2RmWywic291cmNlX25vZGUiXSVpbiVtb2R1bGVfdHJhbnNjcmlwdHNfdGVfZDYgJiB0ZV9kNl9pbnRlcmFjdGlvbl9kZlssInRhcmdldF9ub2RlIl0laW4lbW9kdWxlX3RyYW5zY3JpcHRzX3RlX2Q2LGMoInNvdXJjZV9ub2RlIiwidGFyZ2V0X25vZGUiKV0NCg0KeHl6PU5VTEwNCmZvciAoaSBpbiB0ZV8zZF9hcnJvd19ybmFfcm5hWywic291cmNlX25vZGUiXSl7DQogIHh5ej1yYmluZCh4eXosdGVfZDZfcm5hX21vZHVsZV9hY3AkeFtyb3duYW1lcyh0ZV9kNl9ybmFfbW9kdWxlX2FjcCR4KT09aSxjKDE6MyldKzUpICANCn0NCg0KDQoNCnh5el9lbmQ9TlVMTA0KZm9yIChpIGluIHRlXzNkX2Fycm93X3JuYV9ybmFbLCJ0YXJnZXRfbm9kZSJdKXsNCiAgeHl6X2VuZD1yYmluZCh4eXpfZW5kLHRlX2Q2X3JuYV9tb2R1bGVfYWNwJHhbcm93bmFtZXModGVfZDZfcm5hX21vZHVsZV9hY3AkeCk9PWksYygxOjMpXSs1KSAgDQp9DQoNCnRlX3JuYV9ybmFfM2RfYXJyb3dzPWRhdGEuZnJhbWUodGVfM2RfYXJyb3dfcm5hX3JuYSx4XzNkX2Fycm93PXh5elssMV0seV8zZF9hcnJvdz14eXpbLDJdLCB6XzNkX2Fycm93PXh5elssM10seGVuZF8zZF9hcnJvdz14eXpfZW5kWywxXSx5ZW5kXzNkX2Fycm93PXh5el9lbmRbLDJdLHplbmRfM2RfYXJyb3c9eHl6X2VuZFssM10pDQoNCg0KDQoNCg0KDQoNCg0KDQplcGlfZDZfbW9kdWxlXzNkX2Fycm93cz1OVUxMDQoNCiAgDQplcGlfM2RfYXJyb3dfZG5hX3JuYT1lcGlfZDZfaW50ZXJhY3Rpb25fZGZbZXBpX2Q2X2ludGVyYWN0aW9uX2RmWywic291cmNlX25vZGUiXSVpbiVtb2R1bGVfcHJvbW90ZXJzX2VwaV9kNiAmIGVwaV9kNl9pbnRlcmFjdGlvbl9kZlssInRhcmdldF9ub2RlIl0laW4lbW9kdWxlX3RyYW5zY3JpcHRzX2VwaV9kNixjKCJzb3VyY2Vfbm9kZSIsInRhcmdldF9ub2RlIildDQoNCnh5ej1OVUxMDQpmb3IgKGkgaW4gZXBpXzNkX2Fycm93X2RuYV9ybmFbLCJzb3VyY2Vfbm9kZSJdKXsNCiAgeHl6PXJiaW5kKHh5eixlcGlfZDZfZG5hX21vZHVsZV9hY3AkeFtyb3duYW1lcyhlcGlfZDZfZG5hX21vZHVsZV9hY3AkeCk9PWksYygxOjMpXSkgIA0KfQ0KDQoNCg0KeHl6X2VuZD1OVUxMDQpmb3IgKGkgaW4gZXBpXzNkX2Fycm93X2RuYV9ybmFbLCJ0YXJnZXRfbm9kZSJdKXsNCiAgeHl6X2VuZD1yYmluZCh4eXpfZW5kLGVwaV9kNl9ybmFfbW9kdWxlX2FjcCR4W3Jvd25hbWVzKGVwaV9kNl9ybmFfbW9kdWxlX2FjcCR4KT09aSxjKDE6MyldKzUpICANCn0NCg0KZXBpX2RuYV9ybmFfM2RfYXJyb3dzPWRhdGEuZnJhbWUoZXBpXzNkX2Fycm93X2RuYV9ybmEseF8zZF9hcnJvdz14eXpbLDFdLHlfM2RfYXJyb3c9eHl6WywyXSwgel8zZF9hcnJvdz14eXpbLDNdLHhlbmRfM2RfYXJyb3c9eHl6X2VuZFssMV0seWVuZF8zZF9hcnJvdz14eXpfZW5kWywyXSx6ZW5kXzNkX2Fycm93PXh5el9lbmRbLDNdKQ0KDQoNCg0KDQoNCnBlX2Q4X21vZHVsZV8zZF9hcnJvd3M9TlVMTA0KDQogIA0KcGVfM2RfYXJyb3dfZG5hX3JuYT1wZV9kOF9pbnRlcmFjdGlvbl9kZltwZV9kOF9pbnRlcmFjdGlvbl9kZlssInNvdXJjZV9ub2RlIl0laW4lbW9kdWxlX3Byb21vdGVyc19wZV9kOCAmIHBlX2Q4X2ludGVyYWN0aW9uX2RmWywidGFyZ2V0X25vZGUiXSVpbiVtb2R1bGVfdHJhbnNjcmlwdHNfcGVfZDgsYygic291cmNlX25vZGUiLCJ0YXJnZXRfbm9kZSIpXQ0KDQp4eXo9TlVMTA0KZm9yIChpIGluIHBlXzNkX2Fycm93X2RuYV9ybmFbLCJzb3VyY2Vfbm9kZSJdKXsNCiAgeHl6PXJiaW5kKHh5eixwZV9kOF9kbmFfbW9kdWxlX2FjcCR4W3Jvd25hbWVzKHBlX2Q4X2RuYV9tb2R1bGVfYWNwJHgpPT1pLGMoMTozKV0pICANCn0NCg0KDQoNCnh5el9lbmQ9TlVMTA0KZm9yIChpIGluIHBlXzNkX2Fycm93X2RuYV9ybmFbLCJ0YXJnZXRfbm9kZSJdKXsNCiAgeHl6X2VuZD1yYmluZCh4eXpfZW5kLHBlX2Q4X3JuYV9tb2R1bGVfYWNwJHhbcm93bmFtZXMocGVfZDhfcm5hX21vZHVsZV9hY3AkeCk9PWksYygxOjMpXSs1KSAgDQp9DQoNCnBlX2RuYV9ybmFfM2RfYXJyb3dzPWRhdGEuZnJhbWUocGVfM2RfYXJyb3dfZG5hX3JuYSx4XzNkX2Fycm93PXh5elssMV0seV8zZF9hcnJvdz14eXpbLDJdLCB6XzNkX2Fycm93PXh5elssM10seGVuZF8zZF9hcnJvdz14eXpfZW5kWywxXSx5ZW5kXzNkX2Fycm93PXh5el9lbmRbLDJdLHplbmRfM2RfYXJyb3c9eHl6X2VuZFssM10pDQoNCg0KDQoNCg0KdGVfZDZfbW9kdWxlXzNkX2Fycm93cz1OVUxMDQoNCiAgDQp0ZV8zZF9hcnJvd19kbmFfcm5hPXRlX2Q2X2ludGVyYWN0aW9uX2RmW3RlX2Q2X2ludGVyYWN0aW9uX2RmWywic291cmNlX25vZGUiXSVpbiVtb2R1bGVfcHJvbW90ZXJzX3RlX2Q2ICYgdGVfZDZfaW50ZXJhY3Rpb25fZGZbLCJ0YXJnZXRfbm9kZSJdJWluJW1vZHVsZV90cmFuc2NyaXB0c190ZV9kNixjKCJzb3VyY2Vfbm9kZSIsInRhcmdldF9ub2RlIildDQoNCnh5ej1OVUxMDQpmb3IgKGkgaW4gdGVfM2RfYXJyb3dfZG5hX3JuYVssInNvdXJjZV9ub2RlIl0pew0KICB4eXo9cmJpbmQoeHl6LHRlX2Q2X2RuYV9tb2R1bGVfYWNwJHhbcm93bmFtZXModGVfZDZfZG5hX21vZHVsZV9hY3AkeCk9PWksYygxOjMpXSkgIA0KfQ0KDQoNCg0KeHl6X2VuZD1OVUxMDQpmb3IgKGkgaW4gdGVfM2RfYXJyb3dfZG5hX3JuYVssInRhcmdldF9ub2RlIl0pew0KICB4eXpfZW5kPXJiaW5kKHh5el9lbmQsdGVfZDZfcm5hX21vZHVsZV9hY3AkeFtyb3duYW1lcyh0ZV9kNl9ybmFfbW9kdWxlX2FjcCR4KT09aSxjKDE6MyldKzUpICANCn0NCg0KdGVfZG5hX3JuYV8zZF9hcnJvd3M9ZGF0YS5mcmFtZSh0ZV8zZF9hcnJvd19kbmFfcm5hLHhfM2RfYXJyb3c9eHl6WywxXSx5XzNkX2Fycm93PXh5elssMl0sIHpfM2RfYXJyb3c9eHl6WywzXSx4ZW5kXzNkX2Fycm93PXh5el9lbmRbLDFdLHllbmRfM2RfYXJyb3c9eHl6X2VuZFssMl0semVuZF8zZF9hcnJvdz14eXpfZW5kWywzXSkNCg0KDQoNCg0KDQoNCmVwaV9kNl9tb2R1bGVfM2RfYXJyb3dzPU5VTEwNCg0KICANCmVwaV8zZF9hcnJvd19ybmFfZG5hPWVwaV9kNl9pbnRlcmFjdGlvbl9kZltlcGlfZDZfaW50ZXJhY3Rpb25fZGZbLCJzb3VyY2Vfbm9kZSJdJWluJW1vZHVsZV90cmFuc2NyaXB0c19lcGlfZDYgJiBlcGlfZDZfaW50ZXJhY3Rpb25fZGZbLCJ0YXJnZXRfbm9kZSJdJWluJW1vZHVsZV9wcm9tb3RlcnNfZXBpX2Q2LGMoInNvdXJjZV9ub2RlIiwidGFyZ2V0X25vZGUiKV0NCg0KeHl6PU5VTEwNCmZvciAoaSBpbiBlcGlfM2RfYXJyb3dfcm5hX2RuYVssInNvdXJjZV9ub2RlIl0pew0KICB4eXo9cmJpbmQoeHl6LGVwaV9kNl9ybmFfbW9kdWxlX2FjcCR4W3Jvd25hbWVzKGVwaV9kNl9ybmFfbW9kdWxlX2FjcCR4KT09aSxjKDE6MyldKzUpICANCn0NCg0KDQoNCnh5el9lbmQ9TlVMTA0KZm9yIChpIGluIGVwaV8zZF9hcnJvd19ybmFfZG5hWywidGFyZ2V0X25vZGUiXSl7DQogIHh5el9lbmQ9cmJpbmQoeHl6X2VuZCxlcGlfZDZfZG5hX21vZHVsZV9hY3AkeFtyb3duYW1lcyhlcGlfZDZfZG5hX21vZHVsZV9hY3AkeCk9PWksYygxOjMpXSkgIA0KfQ0KDQplcGlfcm5hX2RuYV8zZF9hcnJvd3M9ZGF0YS5mcmFtZShlcGlfM2RfYXJyb3dfcm5hX2RuYSx4XzNkX2Fycm93PXh5elssMV0seV8zZF9hcnJvdz14eXpbLDJdLCB6XzNkX2Fycm93PXh5elssM10seGVuZF8zZF9hcnJvdz14eXpfZW5kWywxXSx5ZW5kXzNkX2Fycm93PXh5el9lbmRbLDJdLHplbmRfM2RfYXJyb3c9eHl6X2VuZFssM10pDQoNCg0KDQoNCg0KcGVfZDhfbW9kdWxlXzNkX2Fycm93cz1OVUxMDQoNCiAgDQpwZV8zZF9hcnJvd19ybmFfZG5hPXBlX2Q4X2ludGVyYWN0aW9uX2RmW3BlX2Q4X2ludGVyYWN0aW9uX2RmWywic291cmNlX25vZGUiXSVpbiVtb2R1bGVfdHJhbnNjcmlwdHNfcGVfZDggJiBwZV9kOF9pbnRlcmFjdGlvbl9kZlssInRhcmdldF9ub2RlIl0laW4lbW9kdWxlX3Byb21vdGVyc19wZV9kOCxjKCJzb3VyY2Vfbm9kZSIsInRhcmdldF9ub2RlIildDQoNCnh5ej1OVUxMDQpmb3IgKGkgaW4gcGVfM2RfYXJyb3dfcm5hX2RuYVssInNvdXJjZV9ub2RlIl0pew0KICB4eXo9cmJpbmQoeHl6LHBlX2Q4X3JuYV9tb2R1bGVfYWNwJHhbcm93bmFtZXMocGVfZDhfcm5hX21vZHVsZV9hY3AkeCk9PWksYygxOjMpXSs1KSAgDQp9DQoNCg0KDQp4eXpfZW5kPU5VTEwNCmZvciAoaSBpbiBwZV8zZF9hcnJvd19ybmFfZG5hWywidGFyZ2V0X25vZGUiXSl7DQogIHh5el9lbmQ9cmJpbmQoeHl6X2VuZCxwZV9kOF9kbmFfbW9kdWxlX2FjcCR4W3Jvd25hbWVzKHBlX2Q4X2RuYV9tb2R1bGVfYWNwJHgpPT1pLGMoMTozKV0pICANCn0NCg0KcGVfcm5hX2RuYV8zZF9hcnJvd3M9ZGF0YS5mcmFtZShwZV8zZF9hcnJvd19ybmFfZG5hLHhfM2RfYXJyb3c9eHl6WywxXSx5XzNkX2Fycm93PXh5elssMl0sIHpfM2RfYXJyb3c9eHl6WywzXSx4ZW5kXzNkX2Fycm93PXh5el9lbmRbLDFdLHllbmRfM2RfYXJyb3c9eHl6X2VuZFssMl0semVuZF8zZF9hcnJvdz14eXpfZW5kWywzXSkNCg0KDQoNCg0KDQp0ZV9kNl9tb2R1bGVfM2RfYXJyb3dzPU5VTEwNCg0KICANCnRlXzNkX2Fycm93X3JuYV9kbmE9dGVfZDZfaW50ZXJhY3Rpb25fZGZbdGVfZDZfaW50ZXJhY3Rpb25fZGZbLCJzb3VyY2Vfbm9kZSJdJWluJW1vZHVsZV90cmFuc2NyaXB0c190ZV9kNiAmIHRlX2Q2X2ludGVyYWN0aW9uX2RmWywidGFyZ2V0X25vZGUiXSVpbiVtb2R1bGVfcHJvbW90ZXJzX3RlX2Q2LGMoInNvdXJjZV9ub2RlIiwidGFyZ2V0X25vZGUiKV0NCg0KeHl6PU5VTEwNCmZvciAoaSBpbiB0ZV8zZF9hcnJvd19ybmFfZG5hWywic291cmNlX25vZGUiXSl7DQogIHh5ej1yYmluZCh4eXosdGVfZDZfcm5hX21vZHVsZV9hY3AkeFtyb3duYW1lcyh0ZV9kNl9ybmFfbW9kdWxlX2FjcCR4KT09aSxjKDE6MyldKzUpICANCn0NCg0KDQoNCnh5el9lbmQ9TlVMTA0KZm9yIChpIGluIHRlXzNkX2Fycm93X3JuYV9kbmFbLCJ0YXJnZXRfbm9kZSJdKXsNCiAgeHl6X2VuZD1yYmluZCh4eXpfZW5kLHRlX2Q2X2RuYV9tb2R1bGVfYWNwJHhbcm93bmFtZXModGVfZDZfZG5hX21vZHVsZV9hY3AkeCk9PWksYygxOjMpXSkgIA0KfQ0KDQp0ZV9ybmFfZG5hXzNkX2Fycm93cz1kYXRhLmZyYW1lKHRlXzNkX2Fycm93X3JuYV9kbmEseF8zZF9hcnJvdz14eXpbLDFdLHlfM2RfYXJyb3c9eHl6WywyXSwgel8zZF9hcnJvdz14eXpbLDNdLHhlbmRfM2RfYXJyb3c9eHl6X2VuZFssMV0seWVuZF8zZF9hcnJvdz14eXpfZW5kWywyXSx6ZW5kXzNkX2Fycm93PXh5el9lbmRbLDNdKQ0KDQplcGlfZDZfM2RfYXJyb3dzPXJiaW5kKGVwaV9kbmFfZG5hXzNkX2Fycm93cywgZXBpX2RuYV9ybmFfM2RfYXJyb3dzLCBlcGlfcm5hX3JuYV8zZF9hcnJvd3MsIGVwaV9ybmFfZG5hXzNkX2Fycm93cykNCnBlX2Q4XzNkX2Fycm93cz1yYmluZChwZV9kbmFfZG5hXzNkX2Fycm93cywgcGVfZG5hX3JuYV8zZF9hcnJvd3MsIHBlX3JuYV9ybmFfM2RfYXJyb3dzLCBwZV9ybmFfZG5hXzNkX2Fycm93cykNCnRlX2Q2XzNkX2Fycm93cz1yYmluZCh0ZV9kbmFfZG5hXzNkX2Fycm93cywgdGVfZG5hX3JuYV8zZF9hcnJvd3MsIHRlX3JuYV9ybmFfM2RfYXJyb3dzLCB0ZV9ybmFfZG5hXzNkX2Fycm93cykNCg0KYGBgYA0KDQoNCg0KYGBgYHtyIDNkIG5ldHdvcmtzIHdnY25hIG1vZHVsZSBwYWlyc30NCg0KZXBpX2Q2X21vZHVsZV9hY3A9cmJpbmQoZXBpX2Q2X2RuYV9tb2R1bGVfYWNwJHhbLGMoMTozKV0sIGVwaV9kNl9ybmFfbW9kdWxlX2FjcCR4WyxjKDE6MyldKzUpDQplcGlfZDZfc2l6ZT1jKGVwaV9kNl9kbmFfc2l6ZSxlcGlfZDZfcm5hX3NpemUpDQoNCnNjZW5lID0gbGlzdCgNCiAgeGF4aXMgPSBsaXN0KA0KICB0aXRsZSA9ICIiLA0KICB6ZXJvbGluZSA9IEZBTFNFLA0KICBzaG93bGluZSA9IEZBTFNFLA0KICBzaG93dGlja2xhYmVscyA9IEZBTFNFLA0KICBzaG93Z3JpZCA9IEZBTFNFDQopLA0KICB5YXhpcyA9IGxpc3QoDQogIHRpdGxlID0gIiIsDQogIHplcm9saW5lID0gRkFMU0UsDQogIHNob3dsaW5lID0gRkFMU0UsDQogIHNob3d0aWNrbGFiZWxzID0gRkFMU0UsDQogIHNob3dncmlkID0gRkFMU0UNCiksDQogIHpheGlzID0gbGlzdCgNCiAgdGl0bGUgPSAiIiwNCiAgemVyb2xpbmUgPSBGQUxTRSwNCiAgc2hvd2xpbmUgPSBGQUxTRSwNCiAgc2hvd3RpY2tsYWJlbHMgPSBGQUxTRSwNCiAgc2hvd2dyaWQgPSBGQUxTRQ0KKSkNCg0KDQplcGlfZDZfbW9kdWxlX2FjcF9maWcgPC0gcGxvdF9seSh4PWVwaV9kNl9tb2R1bGVfYWNwWywiUEMxIl0sIHk9ZXBpX2Q2X21vZHVsZV9hY3BbLCJQQzIiXSwgej1lcGlfZDZfbW9kdWxlX2FjcFssIlBDMyJdDQosYWxwaGE9MC41LA0Kc2l6ZSA9IH5sb2coZXBpX2Q2X3NpemUrMSwxMCksIG1hcmtlciA9IGxpc3Qoc3ltYm9sID0gJ2NpcmNsZScsIHNpemVtb2RlID0gJ2RpYW1ldGVyJyksIHNpemVzID0gYygxMywxMjApLGNvbG9yPX5jKHJlcCgiZG5hIixsZW5ndGgobW9kdWxlX3Byb21vdGVyc19lcGlfZDYpKSxyZXAoInJuYSIsbGVuZ3RoKG1vZHVsZV90cmFuc2NyaXB0c19lcGlfZDYpKSksY29sb3JzPWMoImdyZWVuIiwicHVycGxlIikNCikgICU+JSBhZGRfbWFya2VycygNCiAgICAgICAgICAgICAgdGV4dCA9IHJvd25hbWVzKGVwaV9kNl9tb2R1bGVfYWNwKSwNCiAgICAgICAgICAgICAgdGV4dGZvbnQgPSBsaXN0KHNpemU9bG9nMihlcGlfZDZfc2l6ZSsxLjUpKjE1LCBjb2xvcj0gYyhyZXAoImdyZWVuIixsZW5ndGgobW9kdWxlX3Byb21vdGVyc19lcGlfZDYpKSxyZXAoInB1cnBsZSIsbGVuZ3RoKG1vZHVsZV90cmFuc2NyaXB0c19lcGlfZDYpKSkpLA0KICAgICAgICAgICAgICBzaG93bGVnZW5kID0gVCklPiVsYXlvdXQoc2NlbmU9c2NlbmUpIA0KDQplcGlfZDZfbW9kdWxlX2Fycm93X2ZpZz1OVUxMDQoNCmZvciAoaSBpbiBzZXEobnJvdyhyYmluZChlcGlfZG5hX2RuYV8zZF9hcnJvd3MsZXBpX2RuYV9ybmFfM2RfYXJyb3dzKSkpKXsNCnBsb3RfbHkoKSAlPiUNCiAgYWRkX3RyYWNlKHggPSBjKGVwaV9kNl8zZF9hcnJvd3MkeF8zZF9hcnJvd1tpXSxlcGlfZDZfM2RfYXJyb3dzJHhlbmRfM2RfYXJyb3dbaV0pLA0KICAgIHkgPSBjKGVwaV9kNl8zZF9hcnJvd3MkeV8zZF9hcnJvd1tpXSxlcGlfZDZfM2RfYXJyb3dzJHllbmRfM2RfYXJyb3dbaV0pLA0KICAgIHogPSBjKGVwaV9kNl8zZF9hcnJvd3Mkel8zZF9hcnJvd1tpXSxlcGlfZDZfM2RfYXJyb3dzJHplbmRfM2RfYXJyb3dbaV0pLA0KICAgIHR5cGUgPSAic2NhdHRlcjNkIiwgbW9kZSA9ICJsaW5lcyIsIG5hbWUgPSAibGluZXMiLCBzaG93bGVnZW5kID0gRkFMU0UsDQogICAgbGluZT1saXN0KGNvbG9yPSJncmVlbiIsIHdpZHRoID0gMSwgYWxwaGE9MC4xKSkgLT4gZXBpX2Q2X21vZHVsZV9hcnJvd19maWdbW2ldXQ0KfQ0KDQoNCmZvciAoaSBpbiBzZXEobnJvdyhyYmluZChlcGlfZG5hX2RuYV8zZF9hcnJvd3MsZXBpX2RuYV9ybmFfM2RfYXJyb3dzKSksKG5yb3cocmJpbmQoZXBpX2RuYV9kbmFfM2RfYXJyb3dzLGVwaV9kbmFfcm5hXzNkX2Fycm93cykpK25yb3cocmJpbmQoZXBpX3JuYV9ybmFfM2RfYXJyb3dzLGVwaV9ybmFfZG5hXzNkX2Fycm93cykpKSkpew0KcGxvdF9seSgpICU+JQ0KICBhZGRfdHJhY2UoeCA9IGMoZXBpX2Q2XzNkX2Fycm93cyR4XzNkX2Fycm93W2ldLGVwaV9kNl8zZF9hcnJvd3MkeGVuZF8zZF9hcnJvd1tpXSksDQogICAgeSA9IGMoZXBpX2Q2XzNkX2Fycm93cyR5XzNkX2Fycm93W2ldLGVwaV9kNl8zZF9hcnJvd3MkeWVuZF8zZF9hcnJvd1tpXSksDQogICAgeiA9IGMoZXBpX2Q2XzNkX2Fycm93cyR6XzNkX2Fycm93W2ldLGVwaV9kNl8zZF9hcnJvd3MkemVuZF8zZF9hcnJvd1tpXSksDQogICAgdHlwZSA9ICJzY2F0dGVyM2QiLCBtb2RlID0gImxpbmVzIiwgbmFtZSA9ICJsaW5lcyIsIHNob3dsZWdlbmQgPSBGQUxTRSwNCiAgICBsaW5lPWxpc3QoY29sb3I9InB1cnBsZSIsIHdpZHRoID0gMSwgYWxwaGE9MC4xKSklPiVsYXlvdXQoc2NlbmU9c2NlbmUpIC0+IGVwaV9kNl9tb2R1bGVfYXJyb3dfZmlnW1tpXV0NCn0NCg0KZXBpX2Q2X21vZHVsZV9hcnJvd19maWckbm9kZXM9ZXBpX2Q2X21vZHVsZV9hY3BfZmlnDQoNCnN1YnBsb3QoZXBpX2Q2X21vZHVsZV9hcnJvd19maWcpDQoNCg0KcGVfZDhfbW9kdWxlX2FjcD1yYmluZChwZV9kOF9kbmFfbW9kdWxlX2FjcCR4WyxjKDE6MyldLCBwZV9kOF9ybmFfbW9kdWxlX2FjcCR4WyxjKDE6MyldKzUpDQpwZV9kOF9zaXplPWMocGVfZDhfZG5hX3NpemUscGVfZDhfcm5hX3NpemUpDQoNCnNjZW5lID0gbGlzdCgNCiAgeGF4aXMgPSBsaXN0KA0KICB0aXRsZSA9ICIiLA0KICB6ZXJvbGluZSA9IEZBTFNFLA0KICBzaG93bGluZSA9IEZBTFNFLA0KICBzaG93dGlja2xhYmVscyA9IEZBTFNFLA0KICBzaG93Z3JpZCA9IEZBTFNFDQopLA0KICB5YXhpcyA9IGxpc3QoDQogIHRpdGxlID0gIiIsDQogIHplcm9saW5lID0gRkFMU0UsDQogIHNob3dsaW5lID0gRkFMU0UsDQogIHNob3d0aWNrbGFiZWxzID0gRkFMU0UsDQogIHNob3dncmlkID0gRkFMU0UNCiksDQogIHpheGlzID0gbGlzdCgNCiAgdGl0bGUgPSAiIiwNCiAgemVyb2xpbmUgPSBGQUxTRSwNCiAgc2hvd2xpbmUgPSBGQUxTRSwNCiAgc2hvd3RpY2tsYWJlbHMgPSBGQUxTRSwNCiAgc2hvd2dyaWQgPSBGQUxTRQ0KKSkNCg0KDQpwZV9kOF9tb2R1bGVfYWNwX2ZpZyA8LSBwbG90X2x5KHg9cGVfZDhfbW9kdWxlX2FjcFssIlBDMSJdLCB5PXBlX2Q4X21vZHVsZV9hY3BbLCJQQzIiXSwgej1wZV9kOF9tb2R1bGVfYWNwWywiUEMzIl0NCixhbHBoYT0wLjUsDQpzaXplID0gfmxvZyhwZV9kOF9zaXplKzEsMTApLCBtYXJrZXIgPSBsaXN0KHN5bWJvbCA9ICdjaXJjbGUnLCBzaXplbW9kZSA9ICdkaWFtZXRlcicpLCBzaXplcyA9IGMoMTMsMTIwKSxjb2xvcj1+YyhyZXAoImRuYSIsbGVuZ3RoKG1vZHVsZV9wcm9tb3RlcnNfcGVfZDgpKSxyZXAoInJuYSIsbGVuZ3RoKG1vZHVsZV90cmFuc2NyaXB0c19wZV9kOCkpKSxjb2xvcnM9YygiZ3JlZW4iLCJwdXJwbGUiKQ0KKSAgJT4lIGFkZF9tYXJrZXJzKA0KICAgICAgICAgICAgICB0ZXh0ID0gcm93bmFtZXMocGVfZDhfbW9kdWxlX2FjcCksDQogICAgICAgICAgICAgIHRleHRmb250ID0gbGlzdChzaXplPWxvZzIocGVfZDhfc2l6ZSsxLjUpKjE1LCBjb2xvcj0gYyhyZXAoImdyZWVuIixsZW5ndGgobW9kdWxlX3Byb21vdGVyc19wZV9kOCkpLHJlcCgicHVycGxlIixsZW5ndGgobW9kdWxlX3RyYW5zY3JpcHRzX3BlX2Q4KSkpKSwNCiAgICAgICAgICAgICAgc2hvd2xlZ2VuZCA9IFQpJT4lbGF5b3V0KHNjZW5lPXNjZW5lKSANCg0KcGVfZDhfbW9kdWxlX2Fycm93X2ZpZz1OVUxMDQoNCmZvciAoaSBpbiBzZXEobnJvdyhyYmluZChwZV9kbmFfZG5hXzNkX2Fycm93cyxwZV9kbmFfcm5hXzNkX2Fycm93cykpKSl7DQpwbG90X2x5KCkgJT4lDQogIGFkZF90cmFjZSh4ID0gYyhwZV9kOF8zZF9hcnJvd3MkeF8zZF9hcnJvd1tpXSxwZV9kOF8zZF9hcnJvd3MkeGVuZF8zZF9hcnJvd1tpXSksDQogICAgeSA9IGMocGVfZDhfM2RfYXJyb3dzJHlfM2RfYXJyb3dbaV0scGVfZDhfM2RfYXJyb3dzJHllbmRfM2RfYXJyb3dbaV0pLA0KICAgIHogPSBjKHBlX2Q4XzNkX2Fycm93cyR6XzNkX2Fycm93W2ldLHBlX2Q4XzNkX2Fycm93cyR6ZW5kXzNkX2Fycm93W2ldKSwNCiAgICB0eXBlID0gInNjYXR0ZXIzZCIsIG1vZGUgPSAibGluZXMiLCBuYW1lID0gImxpbmVzIiwgc2hvd2xlZ2VuZCA9IEZBTFNFLA0KICAgIGxpbmU9bGlzdChjb2xvcj0iZ3JlZW4iLCB3aWR0aCA9IDEsIGFscGhhPTAuMSkpIC0+IHBlX2Q4X21vZHVsZV9hcnJvd19maWdbW2ldXQ0KfQ0KDQoNCmZvciAoaSBpbiBzZXEobnJvdyhyYmluZChwZV9kbmFfZG5hXzNkX2Fycm93cyxwZV9kbmFfcm5hXzNkX2Fycm93cykpLChucm93KHJiaW5kKHBlX2RuYV9kbmFfM2RfYXJyb3dzLHBlX2RuYV9ybmFfM2RfYXJyb3dzKSkrbnJvdyhyYmluZChwZV9ybmFfcm5hXzNkX2Fycm93cyxwZV9ybmFfZG5hXzNkX2Fycm93cykpKSkpew0KcGxvdF9seSgpICU+JQ0KICBhZGRfdHJhY2UoeCA9IGMocGVfZDhfM2RfYXJyb3dzJHhfM2RfYXJyb3dbaV0scGVfZDhfM2RfYXJyb3dzJHhlbmRfM2RfYXJyb3dbaV0pLA0KICAgIHkgPSBjKHBlX2Q4XzNkX2Fycm93cyR5XzNkX2Fycm93W2ldLHBlX2Q4XzNkX2Fycm93cyR5ZW5kXzNkX2Fycm93W2ldKSwNCiAgICB6ID0gYyhwZV9kOF8zZF9hcnJvd3Mkel8zZF9hcnJvd1tpXSxwZV9kOF8zZF9hcnJvd3MkemVuZF8zZF9hcnJvd1tpXSksDQogICAgdHlwZSA9ICJzY2F0dGVyM2QiLCBtb2RlID0gImxpbmVzIiwgbmFtZSA9ICJsaW5lcyIsIHNob3dsZWdlbmQgPSBGQUxTRSwNCiAgICBsaW5lPWxpc3QoY29sb3I9InB1cnBsZSIsIHdpZHRoID0gMSwgYWxwaGE9MC4xKSklPiVsYXlvdXQoc2NlbmU9c2NlbmUpIC0+IHBlX2Q4X21vZHVsZV9hcnJvd19maWdbW2ldXQ0KfQ0KDQpwZV9kOF9tb2R1bGVfYXJyb3dfZmlnJG5vZGVzPXBlX2Q4X21vZHVsZV9hY3BfZmlnDQoNCnN1YnBsb3QocGVfZDhfbW9kdWxlX2Fycm93X2ZpZykNCg0KDQoNCnRlX2Q2X21vZHVsZV9hY3A9cmJpbmQodGVfZDZfZG5hX21vZHVsZV9hY3AkeFssYygxOjMpXSwgdGVfZDZfcm5hX21vZHVsZV9hY3AkeFssYygxOjMpXSs1KQ0KdGVfZDZfc2l6ZT1jKHRlX2Q2X2RuYV9zaXplLHRlX2Q2X3JuYV9zaXplKQ0KDQpzY2VuZSA9IGxpc3QoDQogIHhheGlzID0gbGlzdCgNCiAgdGl0bGUgPSAiIiwNCiAgemVyb2xpbmUgPSBGQUxTRSwNCiAgc2hvd2xpbmUgPSBGQUxTRSwNCiAgc2hvd3RpY2tsYWJlbHMgPSBGQUxTRSwNCiAgc2hvd2dyaWQgPSBGQUxTRQ0KKSwNCiAgeWF4aXMgPSBsaXN0KA0KICB0aXRsZSA9ICIiLA0KICB6ZXJvbGluZSA9IEZBTFNFLA0KICBzaG93bGluZSA9IEZBTFNFLA0KICBzaG93dGlja2xhYmVscyA9IEZBTFNFLA0KICBzaG93Z3JpZCA9IEZBTFNFDQopLA0KICB6YXhpcyA9IGxpc3QoDQogIHRpdGxlID0gIiIsDQogIHplcm9saW5lID0gRkFMU0UsDQogIHNob3dsaW5lID0gRkFMU0UsDQogIHNob3d0aWNrbGFiZWxzID0gRkFMU0UsDQogIHNob3dncmlkID0gRkFMU0UNCikpDQoNCg0KdGVfZDZfbW9kdWxlX2FjcF9maWcgPC0gcGxvdF9seSh4PXRlX2Q2X21vZHVsZV9hY3BbLCJQQzEiXSwgeT10ZV9kNl9tb2R1bGVfYWNwWywiUEMyIl0sIHo9dGVfZDZfbW9kdWxlX2FjcFssIlBDMyJdDQosYWxwaGE9MC41LA0Kc2l6ZSA9IH5sb2codGVfZDZfc2l6ZSsxLDEwKSwgbWFya2VyID0gbGlzdChzeW1ib2wgPSAnY2lyY2xlJywgc2l6ZW1vZGUgPSAnZGlhbWV0ZXInKSwgc2l6ZXMgPSBjKDEzLDEyMCksY29sb3I9fmMocmVwKCJkbmEiLGxlbmd0aChtb2R1bGVfcHJvbW90ZXJzX3RlX2Q2KSkscmVwKCJybmEiLGxlbmd0aChtb2R1bGVfdHJhbnNjcmlwdHNfdGVfZDYpKSksY29sb3JzPWMoImdyZWVuIiwicHVycGxlIikNCikgICU+JSBhZGRfbWFya2VycygNCiAgICAgICAgICAgICAgdGV4dCA9IHJvd25hbWVzKHRlX2Q2X21vZHVsZV9hY3ApLA0KICAgICAgICAgICAgICB0ZXh0Zm9udCA9IGxpc3Qoc2l6ZT1sb2cyKHRlX2Q2X3NpemUrMS41KSoxNSwgY29sb3I9IGMocmVwKCJncmVlbiIsbGVuZ3RoKG1vZHVsZV9wcm9tb3RlcnNfdGVfZDYpKSxyZXAoInB1cnBsZSIsbGVuZ3RoKG1vZHVsZV90cmFuc2NyaXB0c190ZV9kNikpKSksDQogICAgICAgICAgICAgIHNob3dsZWdlbmQgPSBUKSU+JWxheW91dChzY2VuZT1zY2VuZSkgDQoNCnRlX2Q2X21vZHVsZV9hcnJvd19maWc9TlVMTA0KDQpmb3IgKGkgaW4gc2VxKG5yb3cocmJpbmQodGVfZG5hX2RuYV8zZF9hcnJvd3MsdGVfZG5hX3JuYV8zZF9hcnJvd3MpKSkpew0KcGxvdF9seSgpICU+JQ0KICBhZGRfdHJhY2UoeCA9IGModGVfZDZfM2RfYXJyb3dzJHhfM2RfYXJyb3dbaV0sdGVfZDZfM2RfYXJyb3dzJHhlbmRfM2RfYXJyb3dbaV0pLA0KICAgIHkgPSBjKHRlX2Q2XzNkX2Fycm93cyR5XzNkX2Fycm93W2ldLHRlX2Q2XzNkX2Fycm93cyR5ZW5kXzNkX2Fycm93W2ldKSwNCiAgICB6ID0gYyh0ZV9kNl8zZF9hcnJvd3Mkel8zZF9hcnJvd1tpXSx0ZV9kNl8zZF9hcnJvd3MkemVuZF8zZF9hcnJvd1tpXSksDQogICAgdHlwZSA9ICJzY2F0dGVyM2QiLCBtb2RlID0gImxpbmVzIiwgbmFtZSA9ICJsaW5lcyIsIHNob3dsZWdlbmQgPSBGQUxTRSwNCiAgICBsaW5lPWxpc3QoY29sb3I9ImdyZWVuIiwgd2lkdGggPSAxLCBhbHBoYT0wLjEpKSAtPiB0ZV9kNl9tb2R1bGVfYXJyb3dfZmlnW1tpXV0NCn0NCg0KDQpmb3IgKGkgaW4gc2VxKG5yb3cocmJpbmQodGVfZG5hX2RuYV8zZF9hcnJvd3MsdGVfZG5hX3JuYV8zZF9hcnJvd3MpKSwobnJvdyhyYmluZCh0ZV9kbmFfZG5hXzNkX2Fycm93cyx0ZV9kbmFfcm5hXzNkX2Fycm93cykpK25yb3cocmJpbmQodGVfcm5hX3JuYV8zZF9hcnJvd3MsdGVfcm5hX2RuYV8zZF9hcnJvd3MpKSkpKXsNCnBsb3RfbHkoKSAlPiUNCiAgYWRkX3RyYWNlKHggPSBjKHRlX2Q2XzNkX2Fycm93cyR4XzNkX2Fycm93W2ldLHRlX2Q2XzNkX2Fycm93cyR4ZW5kXzNkX2Fycm93W2ldKSwNCiAgICB5ID0gYyh0ZV9kNl8zZF9hcnJvd3MkeV8zZF9hcnJvd1tpXSx0ZV9kNl8zZF9hcnJvd3MkeWVuZF8zZF9hcnJvd1tpXSksDQogICAgeiA9IGModGVfZDZfM2RfYXJyb3dzJHpfM2RfYXJyb3dbaV0sdGVfZDZfM2RfYXJyb3dzJHplbmRfM2RfYXJyb3dbaV0pLA0KICAgIHR5cGUgPSAic2NhdHRlcjNkIiwgbW9kZSA9ICJsaW5lcyIsIG5hbWUgPSAibGluZXMiLCBzaG93bGVnZW5kID0gRkFMU0UsDQogICAgbGluZT1saXN0KGNvbG9yPSJwdXJwbGUiLCB3aWR0aCA9IDEsIGFscGhhPTAuMSkpJT4lbGF5b3V0KHNjZW5lPXNjZW5lKSAtPiB0ZV9kNl9tb2R1bGVfYXJyb3dfZmlnW1tpXV0NCn0NCg0KdGVfZDZfbW9kdWxlX2Fycm93X2ZpZyRub2Rlcz10ZV9kNl9tb2R1bGVfYWNwX2ZpZw0KDQpzdWJwbG90KHRlX2Q2X21vZHVsZV9hcnJvd19maWcpDQoNCg0KDQpgYGBgDQoNCg0KIyMgTWl4LUtlcm5lbCBhbmFseXNpcw0KDQoNCldlIHdhbnQgbm93IHRvIGdvIGZ1cnRoZXIgYW5kIHVzZSBhIGtlcm5lbCBiYXNlZCBhcHByb2FjaCwgd2hpY2ggY29tcHV0ZXMgYSBtdWx0aS1kaW1lbnNpb24gc3BhY2UgaW4gd2hpY2ggZW1icnlvIGNlbGxzIHdpbGwgcGxhY2UgZ2l2ZW4gdGhlaXIgdmljaW5pdHkgYWNyb3NzIGdlbmUgcHJvbW90ZXIgbWV0aHlsYXRpb24gYW5kIGdlbmUgZXhwcmVzc2lvbi4gVGhpcyBleHBsb3JhdG9yeSBub24tc3VwZXJ2aXNlZCBhbmFseXNpcyB0cmFjZXMgYSBub24tbGluZWFyIGZyb250aWVyIG9mIGRlY2lzaW9uIHRoYXQgYWxsb2NhdGVzIGluZGl2aWR1YWxzIGludG8gZ3JvdXBzIChbTWFyaWV0dGUgYW5kIFZpYWxhbmVpeCwgMjAxOF0oI21hcmlldHRlXzIwMTgpKS4NCg0KYGBgYHtyIG1peGtlcm5lbCBmYV9wZmEsIGVjaG89RkFMU0V9DQpkbmFfcm5hX21peF9rZXJuZWw9bGlzdChsb2cyKGRuYV91cG1fc3Vic2V0KzEpLCBsb2cyKHJuYV9zdWJzZXQrMSlbLGNvbG5hbWVzKGRuYV91cG1fc3Vic2V0KV0pDQpuYW1lcyhkbmFfcm5hX21peF9rZXJuZWwpPWMoImRuYSIsInJuYSIpDQoNCg0KIyBjb21wdXRlIGtlcm5lbA0KZG5hX2tlcm5lbCA8LSBjb21wdXRlLmtlcm5lbCh0KGRuYV9ybmFfbWl4X2tlcm5lbCRkbmEpLCBrZXJuZWwuZnVuYyA9ICJsaW5lYXIiKQ0Kcm5hX2tlcm5lbCA8LSBjb21wdXRlLmtlcm5lbCh0KGRuYV9ybmFfbWl4X2tlcm5lbCRybmEpLCBrZXJuZWwuZnVuYyA9ICJsaW5lYXIiKQ0KDQojIGNoZWNrIGRpbWVuc2lvbnMNCmRpbShkbmFfa2VybmVsJGtlcm5lbCkNCmRpbShybmFfa2VybmVsJGtlcm5lbCkNCg0KYGBgYA0KDQpgYGBge3IgbWl4IGtlcm5lbCBleHBsYWluZWQgdmFyaWFuY2UsIG91dC53aWR0aD0iOTAlIiwgZmlnLnRvcGNhcHRpb24gPSBUUlVFICwgZmlnLmNhcD0iPGNlbnRlcj4qKlZhcmlhbmNlIGV4cGxhaW5lZCBieSBrZXJuZWwgZm9yIHRoZSB0d28gZGF0YXNldHMqKjwvY2VudGVyPiJ9DQoNCmNpbS5rZXJuZWwoZG5hID0gZG5hX2tlcm5lbCwNCiAgICAgICAgICAgcm5hID0gcm5hX2tlcm5lbCwNCiAgICAgICAgICAgbWV0aG9kID0gInNxdWFyZSINCikNCg0Kcm5hX2RuYV9tZXRhX2tlcm5lbCA8LSBjb21iaW5lLmtlcm5lbHMoZG5hID0gZG5hX2tlcm5lbCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBybmEgPSBybmFfa2VybmVsLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImZ1bGwtVU1LTCIpDQoNCmtlcm5lbC5wY2EucmVzdWx0IDwtIGtlcm5lbC5wY2Eocm5hX2RuYV9tZXRhX2tlcm5lbCwgbmNvbXAgPSAxMCkNCg0KIyBzYXZlLmltYWdlKCIyMDIxXzA2XzA5X2VtYnJ5b19rZXJuZWwiKQ0KbG9hZCgiMjAyMV8wNl8wOV9lbWJyeW9fa2VybmVsIikNCmBgYGANCg0KV2UgbmVlZGVkIGEgc3BhY2Ugd2l0aGluIGNlbGxzIHNlZ3JlZ2F0ZSBieSBkZXZlbG9wbWVudGFsIHN0YWdlIGFuZCBsaW5lYWdlIHByb2dyZXNzaW9uIC0+IGtlcm5lbCBhbG1vc3Qgc3VjY2VlZGVkLg0KSW5kZWVkLCBjb250cmFyeSB0byBzaW5nbGUgUENBIG9yIFVNQVAsIGtlcm5lbCByZWNhcGl0dWxhdGVzIGJvdGggZGV2ZWxvcG1lbnRhbCBzdGFnZSBwcm9ncmVzc2lvbiBhbmQgbGluZWFnZSBzcGVjaWZpY2F0aW9uLg0KDQpXZSBjYW4gdXNlIHRoaXMga2VybmVsIHNwYWNlIHRvIHZpc3VhbGl6ZSBtZXRoeWxhdGlvbiBhbmQgZXhwcmVzc2lvbiBvZiBnZW5lcywgbm90YWJseSBXR0NOQSBtb2R1bGUgZ2VuZXMsIHRocm91Z2hvdXQgZWFybHkgZGV2ZWxvcG1lbnQgb2YgdGhlIGh1bWFuIGVtYnJ5by4NCg0KYGBgYHtyIHIgbWl4a2VybmVsIGZhX3BmYSAzZCwgZmlndXJlcy1zaWRlLCBmaWcuc2hvdz0iaG9sZCIsIG91dC53aWR0aD0iOTAlIiwgZmlnLnRvcGNhcHRpb24gPSBUUlVFICwgZmlnLmNhcD0iPGNlbnRlcj4qKjNEIEtlcm5lbCBQQ0EgKEtQQ0EpKio8L2NlbnRlcj4ifQ0KbGluZWFnZV9rZXJuZWw9bGluZWFnZVtyb3duYW1lcyhrZXJuZWwucGNhLnJlc3VsdCR4KV0NCm5leHQzZCgpDQpzY2VuZV9rZXJuZWwgPSBsaXN0KGNhbWVyYSA9IGxpc3QoZXllID0gbGlzdCh4ID0gMCwgeSA9IDAsIHogPSAxLjI1KSkpDQpmaWcgPC0gcGxvdF9seSh4PWtlcm5lbC5wY2EucmVzdWx0JHhbLDFdLHk9a2VybmVsLnBjYS5yZXN1bHQkeFssMl0sej1rZXJuZWwucGNhLnJlc3VsdCR4WywzXSxjb2xvciA9IH5mYWN0b3IobGluZWFnZV9rZXJuZWwsbGV2ZWxzPWMoIkVwaSIsICJQRSIsICJURSIpKSwgY29sb3JzID0gYygicmVkIiwiZ3JlZW4iLCJibHVlIiksYWxwaGE9MC42KQ0KZmlnIDwtIGZpZyAlPiUgYWRkX21hcmtlcnMoKQ0KZmlnDQoNCmRheV9rZXJuZWw9ZGF5W3Jvd25hbWVzKGtlcm5lbC5wY2EucmVzdWx0JHgpXQ0KbmV4dDNkKCkNCnNjZW5lX2tlcm5lbCA9IGxpc3QoY2FtZXJhID0gbGlzdChleWUgPSBsaXN0KHggPSAwLCB5ID0gMCwgeiA9IDEuMjUpKSkNCmZpZyA8LSBwbG90X2x5KHg9a2VybmVsLnBjYS5yZXN1bHQkeFssMV0seT1rZXJuZWwucGNhLnJlc3VsdCR4WywyXSx6PWtlcm5lbC5wY2EucmVzdWx0JHhbLDNdLGNvbG9yID0gfmZhY3RvcihkYXlfa2VybmVsLGxldmVscz1jKCJENiIsICJEOCIsICJEMTAiLCAiRDEyIikpLCBjb2xvcnMgPSBjKCJ5ZWxsb3ciLCJ0dXJxdW9pc2UiLCJwdXJwbGUiLCJyZWQiKSxhbHBoYT0wLjYpDQpmaWcgPC0gZmlnICU+JSBhZGRfbWFya2VycygpDQpmaWcNCg0KDQpwcm9tb3Rlcl9rZXJuZWw9bG9nMihkbmFfdXBtX3N1YnNldFt3aGljaChyb3duYW1lcyhkbmFfdXBtX3N1YnNldCk9PSJQT1U1RjEiKSxdKzEpDQpuZXh0M2QoKQ0Kc2NlbmVfa2VybmVsID0gbGlzdChjYW1lcmEgPSBsaXN0KGV5ZSA9IGxpc3QoeCA9IDAsIHkgPSAwLCB6ID0gMS4yNSkpKQ0KZmlnIDwtIHBsb3RfbHkoeD1rZXJuZWwucGNhLnJlc3VsdCR4WywxXSx5PWtlcm5lbC5wY2EucmVzdWx0JHhbLDJdLHo9a2VybmVsLnBjYS5yZXN1bHQkeFssM10sY29sb3IgPSB+cHJvbW90ZXJfa2VybmVsLGFscGhhPTEsIGNvbG9ycz1jKCJibHVlIiwibGlnaHRibHVlIiwiI0ZGQzBDQiIsInJlZCIpKQ0KZmlnIDwtIGZpZyAlPiUgYWRkX21hcmtlcnMoKQ0KZmlnDQoNCnRyYW5zY3JpcHRfa2VybmVsPWxvZzIocm5hX3N1YnNldFt3aGljaChyb3duYW1lcyhybmFfc3Vic2V0KT09IlBPVTVGMSIpLF0rMSkNCm5leHQzZCgpDQpzY2VuZV9rZXJuZWwgPSBsaXN0KGNhbWVyYSA9IGxpc3QoZXllID0gbGlzdCh4ID0gMCwgeSA9IDAsIHogPSAxLjI1KSkpDQpmaWcgPC0gcGxvdF9seSh4PWtlcm5lbC5wY2EucmVzdWx0JHhbLDFdLHk9a2VybmVsLnBjYS5yZXN1bHQkeFssMl0sej1rZXJuZWwucGNhLnJlc3VsdCR4WywzXSxjb2xvciA9IH50cmFuc2NyaXB0X2tlcm5lbCxhbHBoYT0xLCBjb2xvcnM9YygiYmx1ZSIsImxpZ2h0Ymx1ZSIsIiNGRkMwQ0IiLCJyZWQiKSkNCmZpZyA8LSBmaWcgJT4lIGFkZF9tYXJrZXJzKCkNCmZpZw0KDQp0ZXJmMV9ybmFfYm94cGxvdD1kYXRhLmZyYW1lKG1ldGFkYXRhLGV4cHI9bG9nMihybmFfc3Vic2V0W3doaWNoKHJvd25hbWVzKHJuYV9zdWJzZXQpPT0iVEVSRjEiKSxdKzEpLGxpbmVhZ2VfZGF5PXBhc3RlMChtZXRhZGF0YSRsaW5lYWdlLCJfIixtZXRhZGF0YSRkYXkpKQ0KDQoNCnRlcmYxX3JuYV9ib3hwbG90JGxpbmVhZ2VfZGF5PWZhY3Rvcih0ZXJmMV9ybmFfYm94cGxvdCRsaW5lYWdlX2RheSxsZXZlbHM9YygiRXBpX0Q2IiwgIkVwaV9EOCIsICJFcGlfRDEwIiwgIlBFX0Q2IiwgIlBFX0Q4IiwgIlBFX0QxMCIsICJQRV9EMTIiLCAiVEVfRDYiLCAiVEVfRDgiLCAiVEVfRDEwIiwgIlRFX0QxMiIpKQ0KDQpnZ3Bsb3QodGVyZjFfcm5hX2JveHBsb3QsYWVzKHg9bGluZWFnZV9kYXkseT1leHByLGZpbGw9bGluZWFnZV9kYXkpKSsNCiAgZ2VvbV9ib3hwbG90KCkrDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjZmY2ZWViIiwiI2YwMWRiZCIsIiNmMDFkOGQiLCIjNWRmNzAwIiwiIzIwY2MyZCIsIiMxMGFlNGEiLCIjMzlhNDU1IiwiIzA0ZjFmYyIsIiMyMGMxZTEiLCJibHVlIiwiZGFya2JsdWUiKSkrDQogIGxhYnModGl0bGU9IlRFUkYxIGdlbmUgZXhwcmVzc2lvbiIpDQoNCkNIRUsyX2RuYV9ib3hwbG90PWRhdGEuZnJhbWUobWV0YWRhdGEsZXhwcj1sb2cyKGRuYV91cG1fc3Vic2V0W3doaWNoKHJvd25hbWVzKGRuYV91cG1fc3Vic2V0KT09IkNIRUsyIiksXSsxKSxsaW5lYWdlX2RheT1wYXN0ZTAobWV0YWRhdGEkbGluZWFnZSwiXyIsbWV0YWRhdGEkZGF5KSkNCg0KQ0hFSzJfZG5hX2JveHBsb3QkbGluZWFnZV9kYXk9ZmFjdG9yKENIRUsyX2RuYV9ib3hwbG90JGxpbmVhZ2VfZGF5LGxldmVscz1jKCJFcGlfRDYiLCAiRXBpX0Q4IiwgIkVwaV9EMTAiLCAiUEVfRDYiLCAiUEVfRDgiLCAiUEVfRDEwIiwgIlBFX0QxMiIsICJURV9ENiIsICJURV9EOCIsICJURV9EMTAiLCAiVEVfRDEyIikpDQoNCmdncGxvdChDSEVLMl9kbmFfYm94cGxvdCxhZXMoeD1saW5lYWdlX2RheSx5PWV4cHIsZmlsbD1saW5lYWdlX2RheSkpKw0KICBnZW9tX2JveHBsb3QoKSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiNmZjZlZWIiLCIjZjAxZGJkIiwiI2YwMWQ4ZCIsIiM1ZGY3MDAiLCIjMjBjYzJkIiwiIzEwYWU0YSIsIiMzOWE0NTUiLCIjMDRmMWZjIiwiIzIwYzFlMSIsImJsdWUiLCJkYXJrYmx1ZSIpKSsNCiAgbGFicyh0aXRsZT0iQ0hFSzIgRE5BIG1ldGh5bGF0aW9uIikNCg0KQ0hFSzJfcm5hX2JveHBsb3Q9ZGF0YS5mcmFtZShtZXRhZGF0YSxleHByPWxvZzIocm5hX3N1YnNldFt3aGljaChyb3duYW1lcyhybmFfc3Vic2V0KT09IkNIRUsyIiksXSsxKSxsaW5lYWdlX2RheT1wYXN0ZTAobWV0YWRhdGEkbGluZWFnZSwiXyIsbWV0YWRhdGEkZGF5KSkNCg0KQ0hFSzJfcm5hX2JveHBsb3QkbGluZWFnZV9kYXk9ZmFjdG9yKENIRUsyX3JuYV9ib3hwbG90JGxpbmVhZ2VfZGF5LGxldmVscz1jKCJFcGlfRDYiLCAiRXBpX0Q4IiwgIkVwaV9EMTAiLCAiUEVfRDYiLCAiUEVfRDgiLCAiUEVfRDEwIiwgIlBFX0QxMiIsICJURV9ENiIsICJURV9EOCIsICJURV9EMTAiLCAiVEVfRDEyIikpDQoNCmdncGxvdChDSEVLMl9ybmFfYm94cGxvdCxhZXMoeD1saW5lYWdlX2RheSx5PWV4cHIsZmlsbD1saW5lYWdlX2RheSkpKw0KICBnZW9tX2JveHBsb3QoKSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiNmZjZlZWIiLCIjZjAxZGJkIiwiI2YwMWQ4ZCIsIiM1ZGY3MDAiLCIjMjBjYzJkIiwiIzEwYWU0YSIsIiMzOWE0NTUiLCIjMDRmMWZjIiwiIzIwYzFlMSIsImJsdWUiLCJkYXJrYmx1ZSIpKSsNCiAgbGFicyh0aXRsZT0iQ0hFSzIgZ2VuZSBleHByZXNzaW9uIikNCg0KDQoNCmBgYGANCg0KYGBgYHtyIGtlcm5lbCBwbG90IHBjYSByZXMsIGZpZ3VyZXMtc2lkZSwgZmlnLnNob3c9ImhvbGQiLCBvdXQud2lkdGg9IjUwJSIsIGZpZy50b3BjYXB0aW9uID0gVFJVRSAsIGZpZy5jYXA9IjxjZW50ZXI+KipWYXJpYW5jZSBleHBsYWluZWQgYnkgUEMgb2YgS1BDQSoqPC9jZW50ZXI+In0NCnBsb3Qoa2VybmVsLnBjYS5yZXN1bHQpDQpgYGBgDQoNCkNsZWFybHksIHRoZSBrZXJuZWwgUENBIChLUENBKSBzaG93cyBhIHNwYWNlIGluIHdoaWNoIGVtYnJ5b25pYyBjZWxsIHZpY2luaXR5IHJlY2FwaXR1bGF0ZXMgYm90aCBsaW5lYWdlcyBhbmQgZGV2ZWxvcG1lbnRhbCBkYXkuIFRoaXMgaXMgb2YgdXRtb3N0IGludGVyZXN0IGFzIHdlIGNvdWxkIG5vdCBjb21iaW5lIHRoZXNlIHR3byB0cmFpdHMgd2l0aCBzdWZmaWNpZW50IHJlc29sdXRpb24uDQoNClRoZXJlZm9yZSwgdGhlIGtlcm5lbCBiYXNlZCBhcHByb2FjaCBpbnRlZ3JhdGVzIGdlbmUgcHJvbW90ZXIgbWV0aHlsYXRpb24gd2l0aCBnZW5lIGV4cHJlc3Npb24gYW5kIGNvbXB1dGVzIGEgc3BhY2UgcmVjYXBpdHVsYXRpbmcgZGV2ZWxvcG1lbnRhbCBwcm9ncmVzc2lvbiBhbmQgbGluZWFnZSBzcGVjaWZpY2F0aW9uIG9mIHRoZSBlYXJseSBodW1hbiBlbWJyeW8uDQoNCg0KIyBQRVJTUEVDVElWRSA6IE1hY2hpbmUgbGVhcm5pbmcgLT4gYWxnb3JpdGhtIHRoYXQgY2FuIGFzc2lnbiByb2J1c3RseSBjZWxsIGxpbmVhZ2VzIGFuZCBkZXZlbG9wbWVudGFsIHRpbWUNCldlIGNhbiB1c2UgdGhpcyBtb2RlbCB0byBidWlsZCBhbiBhbGdvcml0aG0gdGhhdCBhc3NpZ24gYSBnaXZlbiBlbWJyeW8gY2VsbCB0byBsaW5lYWdlIGFuZCBkZXZlbG9wbWVudGFsIHN0YWdlLCBwcm92aWRlZCBETkEgbWV0aHlsYXRpb24gYW5kIGdlbmUgZXhwcmVzc2lvbiBtZWFzdXJlcy4NCg0KTWFjaGluZSBsZWFybmluZyBtb2RlbDoNCg0KLSBjcmVhdGUNCi0gdHJhaW4NCi0gdGVzdCAob24gb3RoZXIgZGF0YXNldHMgdG9vLCBzaG91bGQgd29yayBmb3Igc2luZ2xlIGRhdGFzZXQgYW5kIGJvdGggZGF0YXNldCBpbnB1dCkNCi0gdXNlDQoNCkEgcHJlZGljdGl2ZSBtb2RlbCBjb3VsZCBoZWxwIHRvIHJlc29sdmUgaW50ZXItZW1icnlvIGRldmVsb3BtZW50YWwgZGVsYXlzIG9yIG1pc2xlYWRlZCBhbm5vdGF0aW9uLCBhbmQgdGh1cyBhY2N1cmF0ZSBkZXZlbG9wbWVudGFsIHN0YWdlIGFuZCBsaW5lYWdlIG1hdHVyYXRpb24gaWRlbnRpZmljYXRpb24uIFRoaXMgYWxzbyBjb3VsZCBoZWxwIHRvIHVuaWZ5IHRoZSBkaXZlcnNlIGRhdGFzZXRzIG9uIGEgY29tbW9uIGFubm90YXRpb24sIHRodXMgaGVscGluZyB0byBjb21wYXJlIHRoZXNlIGRhdGEuDQoNCkZvciB0aGlzIHB1cnBvc2UsIHdlIGNvdWxkIHN1YnNldCB0aGUgY3VycmVudCBkYXRhc2V0IGFuZCB1c2UgdGhlIGtlcm5lbCBzcGFjZSBhbmQgdGhlIFdHQ05BIG1vZHVsZXMsIHRvIHRyYWluIHRoZSBtb2RlbCBvbiBvbmUgc2V0IG9mIGNlbGxzLCBhbmQgdGVzdCBpdCBvbiB0aGUgb3RoZXIgc2V0LCBib3RoIG9mIHdoaWNoIHJlcHJlc2VudGF0aXZlIG9mIGRldmVsb3BtZW50YWwgZGF5IGFuZCBsaW5lYWdlIGRpc3RyaWJ1dGlvbi4NCg0KSG93ZXZlciwgaXQgd291bGQgYmUgYmV0dGVyIHRoYXQgb25jZSBuZXcgZGF0YXNldHMgYXJlIHByb2R1Y2VkLCB3ZSB0ZXN0IG91ciBtb2RlbCBvbiB0aGVzZS4NCg0KIyBEaWZmaWN1bHRpZXMNCg0KLSBubyBhY2Nlc3MgdG8gZmFzdHEgZmlsZXMNCi0gZGlmZmljdWx0aWVzIHRvIG5vcm1hbGlzZSBkYXRhIHByb3Blcmx5DQotIHJlc3VsdHMgc3Ryb25nbHkgZGVwZW5kIG9uIGlucHV0IGRhdGEgZGlzdHJpYnV0aW9uDQotIGhhcmQgdG8gc2VsZWN0IGFwcHJvcHJpYXRlIHNvZnQtcG93ZXIgdGhyZXNob2xkIGZvciBXR0NOQQ0KLSBmdW5jdGlvbmFsIGVucmljaG1lbnQgZGF0YWJhc2VzIHNlZW0gaW5hY2N1cmF0ZSBmb3IgdGhlIGh1bWFuIGVtYnJ5bw0KDQojIFNlc3Npb24gaW5mbw0KDQpgYGB7ciBzZXNzaW9uX2luZm99DQoNCnNlc3Npb25JbmZvKCkNCg0KYGBgDQoNCiMgUmVmZXJlbmNlcw0KDQojIyMjIyMgQXJnZWxhZ3VldCwgUi4sIFZlbHRlbiwgQi4sIEFybm9sLCBELiwgRGlldHJpY2gsIFMuLCBaZW56LCBULiwgTWFyaW9uaSwgSi4gQy4sIEJ1ZXR0bmVyLCBGLiwgSHViZXIsIFcuLCAmIFN0ZWdsZSwgTy4gKDIwMTgpLiBNdWx0aS1PbWljcyBGYWN0b3IgQW5hbHlzaXMtYSBmcmFtZXdvcmsgZm9yIHVuc3VwZXJ2aXNlZCBpbnRlZ3JhdGlvbiBvZiBtdWx0aS1vbWljcyBkYXRhIHNldHMuIE1vbGVjdWxhciBzeXN0ZW1zIGJpb2xvZ3ksIDE0KDYpLCBlODEyNC4gW2h0dHBzOi8vZG9pLm9yZy8xMC4xNTI1Mi9tc2IuMjAxNzgxMjRdKGh0dHBzOi8vZG9pLm9yZy8xMC4xNTI1Mi9tc2IuMjAxNzgxMjQpIHsjYXJnZWxhZ3VldF8yMDE4fQ0KDQojIyMjIyMgSmFza293aWFrLCBQLiBBLiwgQ2FtcGVsbG8sIFIuIEouLCAmIENvc3RhLCBJLiBHLiAoMjAxNCkuIE9uIHRoZSBzZWxlY3Rpb24gb2YgYXBwcm9wcmlhdGUgZGlzdGFuY2VzIGZvciBnZW5lIGV4cHJlc3Npb24gZGF0YSBjbHVzdGVyaW5nLiBCTUMgYmlvaW5mb3JtYXRpY3MsIDE1IFN1cHBsIDIoU3VwcGwgMiksIFMyLiBbaHR0cHM6Ly9kb2kub3JnLzEwLjExODYvMTQ3MS0yMTA1LTE1LVMyLVMyXShodHRwczovL2RvaS5vcmcvMTAuMTE4Ni8xNDcxLTIxMDUtMTUtUzItUzIpIHsjamFza293aWFrXzIwMTR9DQoNCg0KIyMjIyMjIEtvYmFrLCBELiwgTGluZGVybWFuLCBHLkMuIEluaXRpYWxpemF0aW9uIGlzIGNyaXRpY2FsIGZvciBwcmVzZXJ2aW5nIGdsb2JhbCBkYXRhIHN0cnVjdHVyZSBpbiBib3RoIHQtU05FIGFuZCBVTUFQLiBOYXQgQmlvdGVjaG5vbCAzOSwgMTU24oCTMTU3ICgyMDIxKS4gW2h0dHBzOi8vZG9pLW9yZy5wcm94eS5pbnNlcm1iaWJsaW8uaW5pc3QuZnIvMTAuMTAzOC9zNDE1ODctMDIwLTAwODA5LXpdKGh0dHBzOi8vZG9pLW9yZy5wcm94eS5pbnNlcm1iaWJsaW8uaW5pc3QuZnIvMTAuMTAzOC9zNDE1ODctMDIwLTAwODA5LXopIHsja29iYWtfMjAyMX0NCg0KIyMjIyMjIExhbmdmZWxkZXIsIFAuLCBIb3J2YXRoLCBTLiBXR0NOQTogYW4gUiBwYWNrYWdlIGZvciB3ZWlnaHRlZCBjb3JyZWxhdGlvbiBuZXR3b3JrIGFuYWx5c2lzLiBCTUMgQmlvaW5mb3JtYXRpY3MgOSwgNTU5ICgyMDA4KS4gW2h0dHBzOi8vZG9pLm9yZy8xMC4xMTg2LzE0NzEtMjEwNS05LTU1OV0oaHR0cHM6Ly9kb2kub3JnLzEwLjExODYvMTQ3MS0yMTA1LTktNTU5KSB7I2xhbmdmZWxkZXJfMjAwOH0NCg0KIyMjIyMjIExhd2xvciwgTi4sIEZhYmJyaSwgQS4sIEd1YW4sIFAuLCBHZW9yZ2UsIEouLCAmIEthcnV0dXJpLCBSLiBLLiAoMjAxNikuIG11bHRpQ2x1c3Q6IEFuIFItcGFja2FnZSBmb3IgSWRlbnRpZnlpbmcgQmlvbG9naWNhbGx5IFJlbGV2YW50IENsdXN0ZXJzIGluIENhbmNlciBUcmFuc2NyaXB0b21lIFByb2ZpbGVzLiBDYW5jZXIgaW5mb3JtYXRpY3MsIDE1LCAxMDPigJMxMTQuIFtodHRwczovL2RvaS5vcmcvMTAuNDEzNy9DSU4uUzM4MDAwXShodHRwczovL2RvaS5vcmcvMTAuNDEzNy9DSU4uUzM4MDAwKSB7I2xhd2xvcl8yMDE2fQ0KDQojIyMjIyMgTGV2ZXIsIEouLCBLcnp5d2luc2tpLCBNLiAmIEFsdG1hbiwgTi4gUHJpbmNpcGFsIGNvbXBvbmVudCBhbmFseXNpcy4gTmF0IE1ldGhvZHMgMTQsIDY0MeKAkzY0MiAoMjAxNykuIFtodHRwczovL2RvaS5vcmcvMTAuMTAzOC9ubWV0aC40MzQ2XShodHRwczovL2RvaS5vcmcvMTAuMTAzOC9ubWV0aC40MzQ2KSB7I2xldmVyXzIwMTd9DQoNCiMjIyMjIyBNYXJpZXR0ZSwgSi4sICYgVmlsbGEtVmlhbGFuZWl4LCBOLiAoMjAxOCkuIFVuc3VwZXJ2aXNlZCBtdWx0aXBsZSBrZXJuZWwgbGVhcm5pbmcgZm9yIGhldGVyb2dlbmVvdXMgZGF0YSBpbnRlZ3JhdGlvbi4gQmlvaW5mb3JtYXRpY3MgKE94Zm9yZCwgRW5nbGFuZCksIDM0KDYpLCAxMDA54oCTMTAxNS5baHR0cHM6Ly9kb2kub3JnLzEwLjEwOTMvYmlvaW5mb3JtYXRpY3MvYnR4NjgyXShodHRwczovL2RvaS5vcmcvMTAuMTA5My9iaW9pbmZvcm1hdGljcy9idHg2ODIpIHsjbWFyaWV0dGVfMjAxOH0NCg0KIyMjIyMjIE1vbMOoLCBNLiBBLiwgV2ViZXJsaW5nLCBBLiwgJiBaZXJuaWNrYS1Hb2V0eiwgTS4gKDIwMjApLiBDb21wYXJhdGl2ZSBhbmFseXNpcyBvZiBodW1hbiBhbmQgbW91c2UgZGV2ZWxvcG1lbnQ6IEZyb20genlnb3RlIHRvIHByZS1nYXN0cnVsYXRpb24uIEN1cnJlbnQgdG9waWNzIGluIGRldmVsb3BtZW50YWwgYmlvbG9neSwgMTM2LCAxMTPigJMxMzguIFtodHRwczovL2RvaS5vcmcvMTAuMTAxNi9icy5jdGRiLjIwMTkuMTAuMDAyXShodHRwczovL2RvaS5vcmcvMTAuMTAxNi9icy5jdGRiLjIwMTkuMTAuMDAyKSB7I21vbGVfMjAyMH0NCg0KIyMjIyMjIFpob3UsIEYuLCBXYW5nLCBSLiwgWXVhbiwgUC4sIFJlbiwgWS4sIE1hbywgWS4sIExpLCBSLiwgTGlhbiwgWS4sIExpLCBKLiwgV2VuLCBMLiwgWWFuLCBMLiwgUWlhbywgSi4sICYgVGFuZywgRi4gKDIwMTkpLiBSZWNvbnN0aXR1dGluZyB0aGUgdHJhbnNjcmlwdG9tZSBhbmQgRE5BIG1ldGh5bG9tZSBsYW5kc2NhcGVzIG9mIGh1bWFuIGltcGxhbnRhdGlvbi4gTmF0dXJlLCA1NzIoNzc3MSksIDY2MOKAkzY2NC4gW2h0dHBzOi8vZG9pLm9yZy8xMC4xMDM4L3M0MTU4Ni0wMTktMTUwMC0wXShodHRwczovL2RvaS5vcmcvMTAuMTAzOC9zNDE1ODYtMDE5LTE1MDAtMCkgeyN6aG91XzIwMTl9DQoNCiMjIyMjIyBab3BwaSwgSi4sIEd1aWxsYXVtZSwgSkYuLCBOZXVubGlzdCwgTS4gZXQgYWwuIE1pQmlPbWljczogYW4gaW50ZXJhY3RpdmUgd2ViIGFwcGxpY2F0aW9uIGZvciBtdWx0aS1vbWljcyBkYXRhIGV4cGxvcmF0aW9uIGFuZCBpbnRlZ3JhdGlvbi4gQk1DIEJpb2luZm9ybWF0aWNzIDIyLCA2ICgyMDIxKS4gW2h0dHBzOi8vZG9pLm9yZy8xMC4xMTg2L3MxMjg1OS0wMjAtMDM5MjEtOF0oaHR0cHM6Ly9kb2kub3JnLzEwLjExODYvczEyODU5LTAyMC0wMzkyMS04KSB7I3pvcHBpXzIwMjF9DQo=